instructions

I had a strange problem with my colleague’s development yesterday.

Using Guava as A cache, store A List, call it List A for the sake of description, fetch it from another location, and perform the difference set with the elements in List B. In simple terms, it looks like this:

public class ArrayListTest {
    // For convenience, we use HashMap for caching
    private Map<String, List<Long>> cache = new HashMap<>();
    
    private void save(a){
        List<Long> listA = createListA();
        cache.put("listA", listA);
    }
    
    private void get(a){
        List<Long> listB = createListB();
        List<Long> listA = cache.get("listA");
        listA.removeAll(listB);
    }
    
    private List<Long> createListA(a){...}private List<Long> createListB(a){...}public static void main(String[] args){
        ArrayListTest test = newArrayListTest(); test.save(); test.get(); }}Copy the code

The save method is called, then the get method is called, and then the exception is thrown:

Exception in thread "main" java.lang.UnsupportedOperationException
	at java.util.AbstractList.remove(AbstractList.java:161)
	at java.util.AbstractList$Itr.remove(AbstractList.java:374)
	at java.util.AbstractCollection.removeAll(AbstractCollection.java:376)
    ...
Copy the code

To explore problems

Whether it was the loss of humanity or the loss of morality, even a small List could not be played. In the face of the sudden blow, my colleagues and I began to reflect on it, copying and pasting was fun for a while, and debug the crematorium.

But as a good programmer, how can you be bothered by this difficulty? So began the troubleshooting tour.

Let’s see if I have any misconceptions about ArrayList:

@Test
public void testArrayList(a) {
    List<Long> listA = new ArrayList<>();
    listA.add(1L);
    listA.add(2L);
    List<Long> listB = new ArrayList<>();
    listB.add(2L);
    listB.add(3L);
    listA.removeAll(listB);
    System.out.println(JSON.toJSONString(listA));
}
Copy the code

The output is as follows:

[1]
Copy the code

Well, it seems not.

Back to see, is the exception thrown UnsupportedOperationException abnormalities, and it is thrown in the AbstractList, he opens the source AbstractList.

public E remove(int index) {
    throw new UnsupportedOperationException();
}
Copy the code

The AbstractList class’s default implementation of the remove method is to throw an exception directly, so if subclasses do not override the method, the above problem will occur.

The problem, then, should be the way list A is created.

Arrays.aslist () creates list A from arrays.asList ()

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

This is also creating an ArrayList, which, in a sense, should be fine, but wait, an ArrayList doesn’t have a constructor that can pass in variable-length arguments, so I take a little bit of the ArrayList and finally see what’s wrong.

The ArrayList object created from arrays.asList () is created by instantiating the ArrayList inner class of Arrays, so this ArrayList is not the usual ArrayList.

This inner class does not override the remove method of its AbstractList parent, so it calls the remove method of its AbstractList parent directly, thus causing the above exception.

The correct way to open arrays.aslist

To make better use of this method, let’s take a look at its comments:

 /**
* Returns a fixed-size list backed by the specified array.  (Changes to
* the returned list "write through" to the array.)  This method acts
* as bridge between array-based and collection-based APIs, in
* combination with {@link Collection#toArray}. The returned list is* serializable and implements {@link RandomAccess}. * * <p>This method also provides a convenient way to create a fixed-size * list initialized to contain several elements: * <pre> * List&lt; String&gt; stooges = Arrays.asList("Larry"."Moe"."Curly");
* </pre>
*
* @param <T> the class of the objects in the array
* @param a the array by which the list will be backed
* @return a list view of the specified array
*/
Copy the code

As can be seen from the description, there are several points to note:

1. This method returns a list of fixed length

So its length cannot be changed, so it cannot add or remove elements. As you can see from the list of methods in its inner class ArrayList, the add and remove methods are not overridden, so calls to both methods will result in exceptions being thrown.

You cannot change the length of a list, but you can change the elements in the list and their positions. For example, the set method is used to reset values, the replaceAll method is used to batch replace, the sort method is used to sort, and so on.

2. Any changes to the list are written back to the original array

That is, any modification to the returned list will result in changes to the original array. To Test this, use a Test:

@Test
public void testArrays(a) {
    Long[] longs = {1L.2L.4L.3L};
    List<Long> longList = Arrays.asList(longs);
    System.out.println("longList:" + JSON.toJSONString(longList) + "longs:" + JSON.toJSONString(longs));

    longList.set(1.5L);
    System.out.println("longList:" + JSON.toJSONString(longList) + "longs:" + JSON.toJSONString(longs));

    longList.replaceAll(a -> a + 1L);
    System.out.println("longList:" + JSON.toJSONString(longList) + "longs:" + JSON.toJSONString(longs));

    longList.sort(Long::compareTo);
    System.out.println("longList:" + JSON.toJSONString(longList) + "longs:" + JSON.toJSONString(longs));

    longs[2] = 7L;
    System.out.println("longList:" + JSON.toJSONString(longList) + "longs:" + JSON.toJSONString(longs));
}
Copy the code

The output is as follows:

Longs,2,4,3 longList: [1] : [1,2,4,3] longList:,5,4,3 [1] longs:,5,4,3 [1] longList:,6,5,4 [2] longs:,6,5,4 [2] Longs,4,5,6 longList: [2], [2,4,5,6] longList:,4,7,6 [2] longs:,4,7,6 [2]Copy the code

Notice that in the last output, when we modify the elements of the original array, we also change the elements of the list. The reason for this, of course, is that the list just wraps the array and ends up pointing to the same memory address, so the changes are also synchronized.

3. You cannot use an array of primitive data types as arguments

Here’s an example:

@Test
public void testArrays2(a) {
    int[] ints = { 1.2.3 };
    List list = Arrays.asList(ints);
    System.out.println(list.size());
}
Copy the code

Instead of an error, it prints 1. Why is that?

Back to the instructions:

@param <T> the class of the objects in the array
Copy the code

The type T of the argument refers to the element type in the array. If the element type in the array is primitive, the entire array is treated as one element. We can modify the chestnut slightly to make it clear.

@Test
public void testArrays2(a) {
    int[] ints = { 1.2.3 };
    System.out.println(ints.getClass());
    List list = Arrays.asList(ints);
    System.out.println(JSON.toJSONString(list));
}
Copy the code

The output is as follows:

The class [I [[1, 2, 3]]Copy the code

Notice that the output of the second line is a two-dimensional array. Variable-length arguments are essentially an array of objects, so if passed an Integer array, they will be received normally:

@Test
public void testArrays2(a) {
    Integer[] ints = { 1.2.3 };
    System.out.println(ints.getClass());
    List list = Arrays.asList(ints);
    System.out.println(list.size());
}
Copy the code
class [Ljava.lang.Integer;
3
Copy the code

conclusion

. At this point, about the Arrays asList () exploration tour ended, encounter problems in general and one with the source code can solve it, but for the commonly used class, if to be unfamiliar with its internal operation mechanism, the code will be prone to some is not in conformity with the expected behavior, an exception is not terrible, because you can according to the abnormal fast positioning, The most afraid is no error, can run normally, but the data processing is wrong, until really found, may have caused irreparable losses.

AsList () is not too hard to use. By extension, modules like Guava and Fastjson, or Spring, Redis, or Dubbo, are not too hard to learn to use. However, if you are not familiar with the internal operation mechanism, Just as a black box, it is impossible to explore the exquisite design inside, and it is difficult to deal with problems. If the function is only framed within the scope of its set capabilities, there is no way to customize the transformation.

Well, I still have a long way to go. Finally with xunzi’s words to mutual encouragement.

“Though the road is long,

It is too small a thing to be done.”