Use with caution in the ArrayList method subList

 

Transfer: https:? //Www.toutiao.com/a6705958780460335619/ tt_from = weixin & utm_campaign = client_share & wxshare_count = 1 & timestamp = 1561423940 & app = news_article & utm_source = weixin & utm_medium = toutiao_android & req_id = 201906250852200101520492020364FCF & group_id = 6705958780460335619

 

Collection of Java developers daily development often used to. In some previous article, we introduce some should be on Using collections note, such as "Why Alibaba ban remove / add operation element in the foreach loop in", "Why Alibaba recommend a set of initialization, the specified collection capacity size "and so on.

About collections, "Ali Baba Java Development Manual" in fact, there is another provision:

Why Alibaba subList method requires careful use of the ArrayList

In this paper, to analyze why there is such a suggestion? What is the principle behind it is?

1 subList

List subList is a method defined in the interface, this method is mainly used to return a set period, can be understood as a collection of some elements, taken in his return value is a List.

As the following code:

Why Alibaba subList method requires careful use of the ArrayList

 

The output of the above code:

[Hollis]

 

If we change the code under the subList return value strong turn into ArrayList try:

Why Alibaba subList method requires careful use of the ArrayList

 

The above code will throw an exception:

java.lang.ClassCastException: java.util.ArrayList$SubList cannot be cast to java.util.ArrayList

Not just turn into a strong ArrayList will complain, strong turn into a LinkedList, Vector, etc. List implementation class also will be given.

So, why this error happen? We next in-depth analysis.

2 underlying principle

First, we look to our subList method returns a List in the end is something, which is in the JDK source code comments had this to say:

Returns a view of the portion of this list between the specifiedfromIndex, inclusive, and toIndex, exclusive.

That is a view subList return, then what is the view of it?

We look subList source:

public List<E> subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);
return new SubList(this, 0, fromIndex, toIndex);
}

This method returns a SubList, this class is an internal class in the ArrayList.

SubList this class is defined separately set, get, size, add, remove the like.

When we call subList method, by creating a SubList will call SubList constructor, then look at what this constructor do things:

SubList(AbstractList<E> parent,
int offset, int fromIndex, int toIndex) {
this.parent = parent;
this.parentOffset = fromIndex;
this.offset = offset + fromIndex;
this.size = toIndex - fromIndex;
this.modCount = ArrayList.this.modCount;
}

It can be seen that the original constructor List and the List of some of the properties assigned to direct some of their own properties a.

In other words, SubList did not re-create a List, but direct reference to the original List (returns a view of the parent class), just specify what range of elements he wants to use it (from fromIndex (inclusive) to toIndex (not included)).

So, why not speak subList method to get a collection of converted directly into ArrayList it? Just because SubList internal ArrayList class, and no inheritance relationship between them, it can not be cast directly.

3 view what is the problem

By looking at the front of the source, we know, subList () method does not re-create an ArrayList, but returned to the interior of an ArrayList class --SubList.

This SubList is a view of the ArrayList.

Well, this view will bring the problem? We simply need to look at the code to write paragraphs.

1, non-structural change SubList

Why Alibaba subList method requires careful use of the ArrayList

 

got the answer:

sourceList : [H, O, L, L, I, S]
sourceList.subList(2, 5) 得到List :
subList : [L, L, I]
subList.set(3,666) 得到List :
subList : [L, 666, I]
sourceList : [H, O, L, 666, I, S]

当我们尝试通过set方法,改变subList中某个元素的值得时候,我们发现,原来的那个List中对应元素的值也发生了改变。

同理,如果我们使用同样的方法,对sourceList中的某个元素进行修改,那么subList中对应的值也会发生改变。读者可以自行尝试一下。

2、结构性改变SubList

Why Alibaba subList method requires careful use of the ArrayList

 

得到结果:

sourceList : [H, O, L, L, I, S]
sourceList.subList(2, 5) 得到List :
subList : [L, L, I]
subList.add(666) 得到List :
subList : [L, L, I, 666]
sourceList : [H, O, L, L, I, 666, S]

我们尝试对subList的结构进行改变,即向其追加元素,那么得到的结果是sourceList的结构也同样发生了改变。

3、结构性改变原List

Why Alibaba subList method requires careful use of the ArrayList

 

得到结果:

Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$SubList.checkForComodification(ArrayList.java:1239)
at java.util.ArrayList$SubList.listIterator(ArrayList.java:1099)
at java.util.AbstractList.listIterator(AbstractList.java:299)
at java.util.ArrayList$SubList.iterator(ArrayList.java:1095)
at java.util.AbstractCollection.toString(AbstractCollection.java:454)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at com.hollis.SubListTest.main(SubListTest.java:28)

我们尝试对sourceList的结构进行改变,即向其追加元素,结果发现抛出了ConcurrentModificationException。关于这个异常,我们在《一不小心就踩坑的fail-fast是个什么鬼?》中分析过,这里原理相同,就不再赘述了。

4 小结

We briefly summarize, List of subList method does not create a new List, but the use of the original List of view, which uses an internal class SubList representation.

So, we can not subList method returns List cast to the ArrayList class, etc., because there is no inheritance relationship between them.

In addition, view and modify the original List also need to pay attention to several points, particularly the interaction between them:

  • 1, the parent (sourceList) promoter (subList) List of non-structural modifications do (non-structural changes), will affect each other.
  • 2, sub-List to make structural changes, the operation will also be reflected in the parent List.
  • 3. List the parent to do structural changes, will throw an exception ConcurrentModificationException.

So, Alibaba Java Developer's Handbook have another one states:

Why Alibaba subList method requires careful use of the ArrayList

5

How to Create a New List

If you need to make changes to subList, I do not want to move original list. You can create a copy of subList:

subList = Lists.newArrayList(subList);
list.stream().skip(strart).limit(end).collect(Collectors.toList());

PS: Recently, the "Ali Baba Java Development Manual" has been officially renamed "Java Development Manual", and released a new version adds 21 new statute, modify the description at 112.

 

References:

https://www.jianshu.com/p/5854851240df https://www.cnblogs.com/ljdblog/p/6251387.html

Guess you like

Origin www.cnblogs.com/zt007/p/11080811.html