GitHub 3.7K Star Java engineers become god’s way, not to know?

GitHub 3.7K Star Java engineers become god’s way, really not to know?

GitHub 3.7K Star Java engineer becomes god’s way, really sure not to check out?

Collections are commonly used in everyday Java development. In previous articles, we have introduced some things to be aware of when using collection classes, such as “Why Alibaba disallows remove/add operations on elements in foreach loops” and “Why Alibaba recommends specifying collection capacity when initializing collections”.

As for collection classes, there is another provision in alibaba Java Development Manual:

This article will analyze why there is such a suggestion? What’s the rationale behind it?

subList

SubList is a method defined in the List interface that returns a List of elements in a collection. SubList is a method that returns a List of elements in a collection.

Such as the following code:

public static void main(String[] args) {
    List<String> names = new ArrayList<String>() {{
        add("Hollis");
        add("hollischuang");
        add("H");
    }};

    List subList = names.subList(0, 1);
    System.out.println(subList);
}
Copy the code

The output result of the above code is:

[Hollis]
Copy the code

If we change the code to force the return value of subList into an ArrayList, try it:

public static void main(String[] args) {
    List<String> names = new ArrayList<String>() {{
        add("Hollis");
        add("hollischuang");
        add("H");
    }};

    ArrayList subList = names.subList(0, 1);
    System.out.println(subList);
}
Copy the code

The above code will throw an exception:

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

Not only does a strong cast of ArrayList fail, but so do implementation classes that force casts of LinkedList, Vector, etc.

So, why does this error occur? Let’s take a closer look.

The underlying principle

First, let’s take a look at what the subList method returns to us. The JDK source comment says this:

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

SubList returns a view, so what is a view?

SubList subList

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

This method returns a SubList, which is an inner class in the ArrayList.

The SubList class defines the set, get, size, add, remove, and other methods separately.

When we call the subList method, we create a subList by calling the subList constructor. Here’s what the constructor does:

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;
}
Copy the code

As you can see, the constructor assigns the original List and some properties of the List directly to some of its own properties.

That is, instead of recreating a List, SubList refers directly to the original List (returning the parent view), specifying the range of elements it wants to use (fromIndex (included), toIndex (not included)).

So why can’t we just convert the collection that we get from the subList method to an ArrayList? Because SubList is only an internal class of ArrayList, there is no integration between them, so you cannot cast them directly.

What’s wrong with views

The subList() method does not recreate an ArrayList. Instead, it returns an ArrayList inner class, subList.

This SubList is a view of the ArrayList.

So what are the problems with this view? We’re going to have to write a couple of simple pieces of code to look at that.

1. Non-structural changes SubList

public static void main(String[] args) { List<String> sourceList = new ArrayList<String>() {{ add("H"); add("O"); add("L"); add("L"); add("I"); add("S"); }}; List subList = sourceList.subList(2, 5); System.out.println("sourceList: "+ sourceList); System.out.println(" sourcelist.sublist (2, 5) get List: "); System.out.println("subList: "+ subList); subList.set(1, "666"); System.out.println(" sublist.set (3,666) gets List: "); System.out.println("subList: "+ subList); System.out.println("sourceList: "+ sourceList); }Copy the code

Results obtained:

SubList: [H, O, L, L, I, S] subList: [L, L, I] subList. Set (3,666) subList: [H, O, L, L, I, S] subList. SubList: [L, 666, I] sourceList: [H, O, L, 666, I, S]Copy the code

When we try to change the value of an element in a subList by using the set method, we find that the value of the corresponding element in the original List is also changed.

Similarly, if we use the same method to modify an element in sourceList, the corresponding value in subList will also change. Readers can try it out for themselves.

1. Structural changes SubList

public static void main(String[] args) {
    List<String> sourceList = new ArrayList<String>() {{
        add("H");
        add("O");
        add("L");
        add("L");
        add("I");
        add("S");
    }};

    List subList = sourceList.subList(2, 5);

    System.out.println("sourceList : " + sourceList);
    System.out.println("sourceList.subList(2, 5) 得到List :");
    System.out.println("subList : " + subList);

    subList.add("666");

    System.out.println("subList.add(666) 得到List :");
    System.out.println("subList : " + subList);
    System.out.println("sourceList : " + sourceList);

}
Copy the code

Results obtained:

SubList: [H, O, L, L, I, S] subList: [L, L, I] subList. Add (666) subList: [H, O, L, L, I, S] subList. SubList: [L, L, I, 666] sourceList: [H, O, L, L, I, 666, S]Copy the code

We tried to change the structure of subList by appending elements to it, and the result was that the structure of sourceList was also changed.

1. Structural changes to the original List

public static void main(String[] args) {
    List<String> sourceList = new ArrayList<String>() {{
        add("H");
        add("O");
        add("L");
        add("L");
        add("I");
        add("S");
    }};

    List subList = sourceList.subList(2, 5);

    System.out.println("sourceList : " + sourceList);
    System.out.println("sourceList.subList(2, 5) 得到List :");
    System.out.println("subList : " + subList);

    sourceList.add("666");

    System.out.println("sourceList.add(666) 得到List :");
    System.out.println("sourceList : " + sourceList);
    System.out.println("subList : " + subList);

}
Copy the code

Results obtained:

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)
Copy the code

We tried to change about the structure of sourceList, namely to the additional elements, the results showed that throw ConcurrentModificationException. About this anomaly, we wrote in “What the Hell is Fail-fast? Here the principle is the same, I will not repeat it.

summary

To summarize, the List subList method does not create a new List. Instead, it uses the view of the original List, which is represented by the inner class subList.

So, we can’t cast a List returned by subList to a class like ArrayList because there is no inheritance relationship between them.

In addition, there are a few things to be aware of when modifying views and the original List, especially how they interact:

1. Non-structural changes made to the parent subList List affect each other.

2. Make structural changes to the child List, and the operation will also be reflected in the parent List.

3, to the parent List for structural modification, throws an exception ConcurrentModificationException.

Therefore, there is another provision in the Alibaba Java development manual:

How do I create a new List

If you need to make changes to subList, you don’t want to touch the original list. Create a copy of subList:

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

PS: Recently, “Alibaba Java Development Manual” has officially changed its name to “Java Development Manual”, and issued a new version, adding 21 new regulations and modifying 112 descriptions.

Attention to the public number background reply: manual, you can get the latest version of the Java development manual.

Reference data: www.jianshu.com/p/585485124… www.cnblogs.com/ljdblog/p/6…