I am kite, the public account “ancient time kite”, a not only technology public account, I have been in the app community for many years, mainly Java, Python, React also play 6 slash developer. The Spring Cloud series is complete, and you can check out the full series on my Github. You can also reply “PDF” in the public account to get the full PDF version of my elaborate tutorial.

Look at the code below, can anyone tell what’s wrong with it?

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

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

He said: Cheng brother, help me to see what is wrong with this code, why the error, nothing operation ah?

Me: It sure looks fine, but I really didn’t use Arrays. AsList. What’s the error?

He: exception information is Java. Lang. UnsupportedOperationException, is thrown when the calls the add method.

Well, I get the idea. This could be another hole in ArrayList, similar to subList.

Arrays.asList

Array.aslist takes a variable generics and returns a List. It’s a nice way to initialize an ArrayList without using {{double parentheses or a new ArrayList. And then I call the add method and I add them one by one. But why is it not mentioned?

While the problem is simple, it’s worth looking at the cause. So, I wrote the above four lines of code to do a test, and it did throw an exception, the exception is as follows:


Go to the arrays.aslist method for a look at the source code.

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

This ArrayList is not another ArrayList. It is an internal class, but it is also called ArrayList.

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(a) {  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) {  returnindexOf(o) ! = -1;  }   @Override  public Spliterator<E> spliterator(a) {  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);  } } Copy the code

This class also inherits AbstractList. However, there is no specific implementation of the add method. Instead, exceptions are thrown out.

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

So arrayLists created by arrays. asList and Arrays are just two subclasses of the same abstract class. Arraylists created by arrays. asList can only be used for simple views. You can’t do too much, so ArrayList doesn’t have arrays.aslist as one of the many ways to initialize an ArrayList.

SubList method

As mentioned above, that problem is similar to subList’s trap in that the object returned is not a true ArrayList type, but a different subclass integrating the same parent class with ArrayList.

One of the pit

So the first pitfall occurs when the object returned by subList is converted to an ArrayList

List<String> stringList = new ArrayList<>();
stringList.add("我");
stringList.add("Yes");
stringList.add("The kite");
List<String> subList = (ArrayList) stringList.subList(0.2);
Copy the code

The following exception is thrown:

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

The reason is clear, because these two are not the same object at all, and there is no inheritance relationship. If there is any relationship, it is more like brother relationship, because both inherit AbstractList.

The second pit

When you’re operating in subList, you’re essentially operating on the original ArrayList, which you might think is a copy list, and then you’re operating like a tiger on subList, and then you turn around and see that the original ArrayList is 250.

For example, the following code adds an element to the subList, then deletes the first element, only to look back at the original ArrayList and find that its results have changed as well.

List<String> stringList = new ArrayList<>();
stringList.add("我");
stringList.add("Yes");
stringList.add("The 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); } Copy the code

Output from the above code:

------------------
Modified subList------------------
isA kite!!!!!!!!!------------------ The original ArrayList------------------ isA kite!!!!!!!!!Copy the code

Why does this happen? Because that’s what subList’s implementation looks like: face coverting. We can take a look at the subList method source.

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

See inside it is a new SubList class, which is a subclass of the ArrayList mentioned above, see the first parameter this, this is the original ArrayList, and all subsequent additions and delets are actually done on this, And that’s what you end up doing on the original list, so everything you do is going to be an honest reflection of the original list, and then you want to use the original list, sorry, I can’t find it anymore.

3 the pit

If you use the subList method to get a subList and then add or delete from the original list, the subList you obtained is invalid and unusable. Unusable means that if you iterate, add, or delete from the subList, you will raise an exception. I can’t even traverse it.

Take the following code for example

List<String> stringList = new ArrayList<>();
stringList.add("我");
stringList.add("Yes");
stringList.add("The kite");

List<String> subList = stringList.subList(0.3); // The number of elements in the original list changes stringList.add("!!!");  / / traverse subList for (String s : subList) {  System.out.println(s); }  / / get elements subList.get(0);  / / remove elements subList.remove(0);  // Add elements subList.add("hello"); Copy the code

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


In fact, for the same reason as the two pit, subList actually operates the original list. When you operate on subList, you will execute the checkForComodification method, which will check whether the number of original lists is the same as the original one. If not, Throw ConcurrentModificationException directly.

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

The last

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


Strong man wait, first give a praise bar, always white piao, the body can not bear!

I am kite, the official number “ancient kite”. A programmer with both depth and breadth of encouragement teacher, a intended to write poetry but write up the code of rural code farmers! You can follow me now, or you can follow history articles later.