[Java required course] easy to use Arrays.asList also has these three holes

Posted by Brendan Nolan on Sun, 20 Oct 2019 15:49:50 +0200

Easy to use asList

In the process of developing or writing test cases, the method of Arrays.asList() is often used, which can quickly and easily convert an array into a List. For example:

List<String> list = Arrays.asList("Book", "Pen", "Desk", "Cup");

When we statically reference Arrays.asList():

import static java.util.Arrays.asList;

You can write as follows:

List<String> list = asList("Book", "Pen", "Desk", "Cup");

Hidden pits

Basic types cannot be generalized

Perform the following test cases:

@Test
public void size() {
  int[] nums = {1, 2, 3, 4, 5, 6};
  List list = asList(nums);
  assertEquals(nums.length, list.size());
}

The result is failed:

java.lang.AssertionError: 
Expected :6
Actual   :1

Why is it clear that it is an array of six elements, and there is only one element after it is converted into a List?

The source code won't lie. Let's look at the code:

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

Through the source code, we can know that the input parameter of asList() method is generic. For int, this basic type cannot be generalized, so the function takes the whole array as a whole (array is of reference type, which can be generalized). The final result is list < int [] > instead of list < integer >.

If we need list < integer >, we can use the following two methods:

@Test
public void listForInt() {
  //Method 1: initialize the array as Integer, and automatically pack it when initializing
  Integer[] nums = {1, 2, 3, 4, 5, 6};
  List<Integer> list = asList(nums);
  assertEquals(nums.length, list.size());
  //Method 2: do not pass in the whole, and automatically pack when processing parameters
  list = asList(1, 2, 3, 4, 5, 6);
  assertEquals(6, list.size());
}

The results of the above two methods are all list < integer >.

Non modifiable

It's transformed into a List happily. I'm going to do a big job and carry out the routine operation of List, but I find that the operation can't be performed:

@Test
public void listAdd() {
  List<String> list = asList("Book", "Pen", "Desk", "Cup");
  list.add("Box");
  assertEquals(5, list.size());
}

The results are as follows:

java.lang.UnsupportedOperationException
    at java.util.AbstractList.add(AbstractList.java:148)
    at java.util.AbstractList.add(AbstractList.java:108)
    at com.larry.basic.AsListTest.listAdd(AsListTest.java:42)

We have to look at the source code again to know that although the result returned by asList() method is ArrayList, it is different from the ArrayList we usually use:

We usually use java.util.ArrayList the most, and the bottom layer is the List of variable Arrays. java.util.Arrays.ArrayList is a static inner class of Arrays, and the bottom layer is the List of final Arrays. They are not the same class.

java.util.Arrays.ArrayList does not override methods such as add/remove/clear, so the method of the parent class AbstractList will be called, and the method of the parent class is as follows:

public boolean add(E e) {
  add(size(), e);
  return true;
}
public void add(int index, E element) {
  throw new UnsupportedOperationException();
}
public E remove(int index) {
  throw new UnsupportedOperationException();
}

Therefore, these methods are actually not callable and throw an exception, unsupported operation exception.

Side effects of modifying operation set

But is the result of asList() really immutable? Not really. Although Arrays.ArrayList does not override the add/remove/clear method, it overrides the set() method:

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

We can replace the elements in it. In fact, it's easy to understand that the bottom layer is the final array, the size is immutable, but the elements of the array are variable. Because of this function, the following problems may arise:

@Test
public void listSet() {
  String[] arr = {"Book", "Pen", "Desk", "Cup"};
  List<String> list = asList(arr);
  list.set(0, "New Book");
  assertEquals("New Book", list.get(0));
  assertEquals("Book", arr[0]);
}

When the first element of the List is changed, the first element of the array is also changed, because they all point to the same array address. If you don't pay attention, you will produce and expect different results.

If you want to create a new List, you can do the following:

List<String> list = new ArrayList<String>(asList(arr));

Because the new ArrayList() will use the method Arrays.copyOf() to copy a new array.

summary

We should also be careful about simple and common things.

Welcome to the public account "pumpkin slow talk", it will keep updating for you...

Topics: Java