Read this article, let you less step on the pits of ArrayList

Keywords: Java Python React Spring

I am a kite, the official account "ancient kite", a technical public official account not only of technology, but also a 6 slash developer who has been in the program circle for many years, and has been playing Java in the main industry, and Python and React are also playing.
The Spring Cloud series has been completed and you can go to My github View the full contents of the series on. You can also reply to the "pdf" in the official account to get my complete pdf version of the complete tutorial.

Look at the code below. Can anyone see what's wrong with it?

String a = "Ancient";
String b  = "kite";
List<String> stringList = Arrays.asList(a,b);
stringList.add("!!!");

This is a question that a little white programmer asked me.

He said: brother Cheng, can you help me to see what's wrong with this code? Why is it wrong? There's no operation?

Me: it doesn't seem to be a problem, but I didn't use it Arrays.asList What's wrong with this method?

He: the abnormal information is java.lang.UnsupportedOperationException , which is thrown when the add method is called.

Well, I've probably understood that this may be another pit of ArrayList. It should be the same as subList.

Arrays.asList

Arrays.asList Method receives a variable length generics, and finally returns a List. It seems to be a very useful method. With it, we always say that the ArrayList initialization method can be more elegant. We don't need the {{double bracket method or the new ArrayList method first, and then call the add method to add them one by one. But why didn't it mention this way?

Although the problem is simple, it is necessary to look at the reasons. So, write the above four lines of code to do a test, and indeed throw an exception when running. The exception is as follows:

Look directly at the source code and locate Arrays.asList Take a look at the methods.

public static <T> List<T> asList(T... a) {
    return new ArrayList<>(a);
}

Eh, it's the new ArrayList. How can it not support the add operation? It's easy to be fooled if you don't look carefully. This ArrayList is not another ArrayList. It's an internal class, but the class name is also called ArrayList. You say that there is no pit.

private static class ArrayList<E> extends AbstractList<E>
        implements RandomAccess, java.io.Serializable {
  
        private static final long serialVersionUID = -2764017481108945198L;
        private final E[] a;

        ArrayList(E[] array) {
            a = Objects.requireNonNull(array);
        }

        @Override
        public int size() {
            return a.length;
        }

        @Override
        public Object[] toArray() {
            return a.clone();
        }

        @Override
        @SuppressWarnings("unchecked")
        public <T> T[] toArray(T[] a) {
            int size = size();
            if (a.length < size)
                return Arrays.copyOf(this.a, size,
                                     (Class<? extends T[]>) a.getClass());
            System.arraycopy(this.a, 0, a, 0, size);
            if (a.length > size)
                a[size] = null;
            return a;
        }

        @Override
        public E get(int index) {
            return a[index];
        }

        @Override
        public E set(int index, E element) {
            E oldValue = a[index];
            a[index] = element;
            return oldValue;
        }

        @Override
        public int indexOf(Object o) {
            E[] a = this.a;
            if (o == null) {
                for (int i = 0; i < a.length; i++)
                    if (a[i] == null)
                        return i;
            } else {
                for (int i = 0; i < a.length; i++)
                    if (o.equals(a[i]))
                        return i;
            }
            return -1;
        }

        @Override
        public boolean contains(Object o) {
            return indexOf(o) != -1;
        }

        @Override
        public Spliterator<E> spliterator() {
            return Spliterators.spliterator(a, Spliterator.ORDERED);
        }

        @Override
        public void forEach(Consumer<? super E> action) {
            Objects.requireNonNull(action);
            for (E e : a) {
                action.accept(e);
            }
        }

        @Override
        public void replaceAll(UnaryOperator<E> operator) {
            Objects.requireNonNull(operator);
            E[] a = this.a;
            for (int i = 0; i < a.length; i++) {
                a[i] = operator.apply(a[i]);
            }
        }

        @Override
        public void sort(Comparator<? super E> c) {
            Arrays.sort(a, c);
        }
}

It defines the basic methods such as set and get, but does not override the add method. This class also inherits the AbstractList, but the add method does not have a specific implementation, but throws out exceptions. The specific logic needs to be implemented by the subclass itself.

public void add(int index, E element) {
    throw new UnsupportedOperationException();
}

So Arrays.asList The ArrayList created by the method and the real ArrayList we usually use are only inherited from two different subclasses of the same abstract class Arrays.asList The created ArrayList can only be used for some simple views, not for too many operations, so there are no initialization methods for ArrayList Arrays.asList That said.

subList method

The problem mentioned above is similar to subList's pit, because the returned object is not the real ArrayList type, but the different subclass integrating the same parent class with ArrayList.

One of the pits

So the first pit is to convert the object returned from subList to ArrayList

List<String> stringList = new ArrayList<>();
stringList.add("I");
stringList.add("yes");
stringList.add("kite");
List<String> subList = (ArrayList) stringList.subList(0, 2);

The following exception is thrown:

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

The reason is very clear, because these two are not an object at all, and there is no inheritance relationship. If there is any relationship, it is at best a brotherhood relationship, because both inherit AbstractList.

Pit two

When you operate in the subList, you are actually operating the original ArrayList. Unidentified students think this is a replica list. Then they operate on the subList like a tiger. Finally, looking back, they see that the original ArrayList has become 250.

For example, in the following code, an element is added to the subList, and then an element at the beginning is deleted. Looking back at the original ArrayList, we find that its result has also changed.

List<String> stringList = new ArrayList<>();
stringList.add("I");
stringList.add("yes");
stringList.add("kite");
List<String> subList = stringList.subList(0, 3);
subList.add("!!!");
subList.remove(0);
System.out.println("------------------");
System.out.println("Modified subList");
System.out.println("------------------");
for (String s : subList) {
    System.out.println(s);
}
System.out.println("------------------");
System.out.println("original ArrayList");
System.out.println("------------------");
for (String a : stringList) {
    System.out.println(a);
}

Output of the above code:

------------------
Modified subList
------------------
yes
 kite
!!!
------------------
Original ArrayList
------------------
yes
 kite
!!!

Why does this happen? Because the implementation of subList is like this. Cover your face. Let's take a look at the source code of subList.

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

See that there is a new SubList class inside it, which is the subclass of ArrayList mentioned above. See the first parameter this, which is the current original ArrayList list, and the subsequent additions and deletions are in fact The operation on this is the operation on the original list at last, so your every move will be honestly reflected on the original list at last, and then you want to use the original list again. Sorry, I can't find it.

Pit three

If you use the subList method to get a subList, and then add or delete it on the original list, that is, the subList you obtained before has been discarded and can't be used, which means that you will throw an exception when you traverse, add or delete the subList. Yes, you can't even traverse it.

For example, the following code

List<String> stringList = new ArrayList<>();
stringList.add("I");
stringList.add("yes");
stringList.add("kite");

List<String> subList = stringList.subList(0, 3);
// Number of original list elements changed
stringList.add("!!!");

// Traverse subList
for (String s : subList) {
    System.out.println(s);
}

// get element
subList.get(0);

// remove element
subList.remove(0);

//Add elements
subList.add("hello");

Traversal, get, remove, and add all throw the following exceptions

In fact, for the same reason as the second pit, the subList actually operates on the original list. When you operate on the subList, the checkForComodification method will be executed. This method will check whether the number of the original list is the same as the original one. If not, a ConcurrentModificationException exception will be thrown directly.

private void checkForComodification() {
    if (ArrayList.this.modCount != this.modCount)
       throw new ConcurrentModificationException();
}

last

Programmers who have not stepped on the JDK pit in the project are not enough to talk about life. Therefore, when you use some seemingly simple and elegant methods, you must be clear about its characteristics and principles, or you will not be far away from the pit.

Wait a moment, hero. Give me a recommendation first. I'm always whoring for nothing. I can't bear it!

I am a kite, the official account is "ancient kite". A programmer with both depth and breadth encourages the teacher, a rural code farmer who originally intended to write poetry but wrote code! You can choose to pay attention to me now, or read the historical articles and pay more attention to them.

Posted by ntg on Thu, 28 May 2020 21:31:19 -0700