In the problems caused by Arrays. asList (), arrays. aslist

Source: Internet
Author: User

In the problems caused by Arrays. asList (), arrays. aslist
Preface

I recently saw a problem on the Internet, which is similar to the following (marked as problem 1 ):

    public class Demo {        public static void main(String[] args) {            System.out.println(testInteger(1));            System.out.println(testInt(1));        }                public static boolean testInteger (Integer num) {            Integer[] nums = new Integer[]{1, 2, 3, 4, 5, 6};            boolean flag = Arrays.asList(nums).contains(num);            return flag;        }                public static boolean testInt (int num) {            int[] nums = new int[]{1, 2, 3, 4, 5, 6};            boolean flag = Arrays.asList(nums).contains(num);            return flag;        }    }

In the result, the first output is true. The questioner thinks it is normal, but the second output is false, which is strange. If I have never used this set in depth or understood generics, I am confused at first glance. Technically, Java has the automatic packing and unpacking function for the basic type and corresponding packaging type. You can use Integer, and I should be able to use int. So I searched for similar problems on the Internet, and found that in addition to the above problems, there are similar situations (marked as problem 2)

    public class Demo2 {        public static void main(String[] args) {            Integer[] nums1 = new Integer[]{1, 2, 3, 4, 5, 6};            List list1 = Arrays.asList(nums1);            list1.set(0, 888);            System.out.println(list1);                        int[] nums2 = new int[]{1, 2, 3, 4, 5, 6};            List list2 = Arrays.asList(nums2);            list2.set(0, 888);            System.out.println(list2);        }    }

The first output is normal, and the first element in the list1 set is modified to 888. However, an error has been reported when the second output is not reached. The complete running result is as follows:

Java. lang. ArrayStoreException: Array Storage exception. The following describes the specific methods and generic queries.

Process

Integer [] nums = new Integer [] {1, 2, 3, 4, 5, 6 };
Arrays. asList (nums); to access this method, the source code is as follows:

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

Just two lines of code, but the content is not simple
1. method parameters are special:Parameters are generic and variable parameters.. A generic type and a variable parameter. To tell the truth, it is useless for beginners or developers to say it is often used, but it is obviously not the same if it is often used. In fact, the above content has already gone wrong in this step. The following will be a detailed analysis.
2. the return value of the method is an ArrayList, which is also a pitfall. This ArrayList is not an ordered set of ArrayList that we have learned or used to do so, but an array tool class.A static internal class of the Arrays class, Inherits AbstractList, as shown below:

Private static class ArrayList <E> extends actlist <E> implements RandomAccess, java. io. serializable {private static final long serialVersionUID =-2764017481108945198L; private final E [] a; ArrayList (E [] array) {// the content of the constructor called above is here, call the requireNonNull method. If the object array is null, a null pointer exception is reported. If it is not null, the value is assigned to member. A = Objects. requireNonNull (array);} // After the value is assigned, a represents the content of the array, so the subsequent methods are basically centered around. @ Override public int size () {return a. length;} @ Override public Object [] toArray () {return a. clone ();}......}

Content of the requireNonNull method:

    public static <T> T requireNonNull(T obj) {        if (obj == null)            throw new NullPointerException();        return obj;    }

After completing this process, we need to consider what we get through the Arrays. asList (nums) method. From the above content, we can first determine that it is a set. In question 1, when the packaging class Integer is used, Arrays. asList (nums) is called. Based on the definition of variable parameters, the length of variable parameters is 5.
In the public static <T> List <T> asList (T... a) method, this is equivalent to specifying generic T as Integer. Then private final E [] a; at this time, the generic E of array a is also Integer. In this case, the content of a is equivalent to new Integer [] {1, 2, 3, 4, 5, 6 };That is, an Integer-type one-dimensional array, and the set is also a List <Integer>

Finally, an Arrays static internal class ArrayList object is obtained. The contains method is rewritten in the internal class:

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

Therefore, in the first case of Problem 1, when 1 is passed in, it will be automatically converted to the Object type, that is, the above o, which is obviously true at this time, so true is returned.
In the second case, false is output. Where is the problem?
In the case of basic type int, it is also based on the definition of variable parameters and the automatic conversion type based on java. Is the length of variable parameters passed in the same as above equal to 5? In fact, it is not. The root cause is that, apart from the definition of variable parameters, there are also generic definitions, but do not forget that generics also have some restrictions. The first point is:
  Generic Type parameters can only be class types (including custom classes), not simple types!
So there are actually differences here, so here we are equivalent to passing in an object, object type is array type, variable parameter length is 1, rather than 5.In this case, array a is regarded as a two-dimensional array. In the above case, the element type of array a is an array, and the number of elements is 1, instead of the Integer above, the set is List <int []>.
The final collection is like this: List <int []>. The length of the set is 1, which contains an array object instead of the mistaken List <Intger>.
Next, call the contains method to set an array-type object, which obviously does not contain 1. Therefore, in case of int in question 1, false is output.
Similarly, in the case of int in question 2, list2.set (0,888); that is, equivalent to the element that indexes the set to 0 (that is, 1st, here the set is an array element) if the value is assigned to an Integer class automatically transformed from 888, an Intger object is assigned to an Array object. Therefore, the preceding Array Storage exception is reported.

Organization and Verification

To better clarify the above content, make the following extensions.

The first is the Integer object type.

Integer [] nums = new Integer [] {1, 2, 3, 4, 5,888}; List <Integer> list2 = Arrays. asList (nums );
System. out. println (list2.get (5); // output 888

Here we successfully obtained the index at this 888.

Next is the basic int type. As mentioned above, since it is a variable parameter, we have passed an array and an array is also an object. Then we can pass in multiple arrays to see it, as shown below.

Int [] nums1 = new int [] {1, 2, 3, 4, 5,666}; int [] nums2 = new int [] {1, 2, 3, 4, 5,777}; int [] nums3 = new int [] {1, 2, 3, 4, 5,888}; List <int []> list1 = Arrays. asList (nums1, nums2, nums3); System. out. println (list1.get (2) [5]); // output 888

Here we click the get method to view the source code:

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

Therefore, the output here is actually a [2] [5], get 888, which also confirms why we mentioned above is essentially a two-dimensional array, it also deepens our understanding of the relationship between the set and the array.

More traps

As mentioned above, we use Arrays. the "ArrayList" obtained by the asList () method is not the commonly used ArrayList. Since it is emphasized that, of course, it is to distinguish, what is the problem if there is no distinction, the following uses a simple Integer as an example:

Integer[] nums = new Integer[]{1, 2, 3, 4, 5};List<Integer> list = Arrays.asList(nums);list.add(888);System.out.println(list);

Add 888 next to the set. What do you think will be printed? [1, 2, 3, 4, 5,888 ]?

The fact is that an exception has been thrown before printing, as shown below:

Java. lang. UnsupportedOperationException: Unsupported operation exception. Why is it not supported? We used to add, remove, and so on. Check the source code.

First, the ArrayList under the java. util package is well known. Its add method is implemented as follows:

    public boolean add(E e) {        ensureCapacityInternal(size + 1);  // Increments modCount!!        elementData[size++] = e;        return true;    }

This is also the reason why we have been operating normally.

Let's take a look at this "ArrayLitst". It does not implement (or accurately rewrite) The add method when inheriting the abstract class AbstractList. Therefore, when calling the add method, it is actually the add method that has been implemented in the called abstract class AbstractList. Let's look at its content:

    public boolean add(E e) {        add(size(), e);        return true;    }

Use add (size (), e); the method that passes in the two parameters is shown as follows:

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

The truth is clear! Throw new UnsupportedOperationException (); Where can this exception be seen clearly. This "ArrayLitst" does not implement the add method at all, so an error is reported. Back to the initial stage, we can also think of the associations between sets and arrays. The length of the array itself is immutable, and here we are essentially operating on the array, so it is not normal to use the add method. By carefully checking the source code of its class, you will find that methods such as remove and delete are not implemented, so an error will also be reported.

If we continue to discuss this, we will not add or delete it here, so I will change the general line. In terms of arrays, it should be supported by theory, and the actual situation is also true. In the "ArrayLitst" internal class, the set method is rewritten. The method can modify the element at the specified index to the specified value, and return the value of the old element as the return value.

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

Note that if we modify an element value for the set here, the content of the original array will change accordingly! That is, through the Arrays. asList () method, the obtained set is associated with the original array. Otherwise, if we modify the array content, the content obtained by the set will also change. Practice:

Integer [] nums = new Integer [] {1, 2, 3, 4, 5}; List <Integer> list = Arrays. asList (nums); list. set (0,888); // modify the set content nums [1] = 999; // modify the array content for (Integer I: nums) {System. out. println (I);} System. out. println (list );

After running, the console outputs the following:

We found that the other party will change either by modifying the array or by modifying the set.

Summary

At first we thought it was a small problem and gradually found that there were a lot of content. The collection was a very common class library in our development, and we were able to optimize our development with a good degree of familiarity. If you want to learn more about the core content such as reflection, you need to make a lot of effort to explore the problem.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.