题目

有这样一道有趣的题目:

final int[] test = new int[]{1,2,3,4};
final Integer[] test2 = new Integer[]{1,2,3,4};
final List list1 = Arrays.asList(test);
final List list2 = Arrays.asList(test2);
final List list3 = Arrays.asList(1,2,3,4);
System.out.println(list1.size());
System.out.println(list2.size());
System.out.println(list3.size());

对于上边的3个size(),输出的结果如下:

1
4
4

这道题考察的是Arrays.asList()这个api以及泛型的知识点,工作时用到该api的情景也挺多的。下面分析下,为什么是这个答案。

分析

对于list1,为什么size是1?

这是因为Arrays.asList如果传入的数组是基础数据类型的数组时,会将整个数组作为一个对象来构建ArrayList,所以size是1。在源码实现中:

public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
} 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;
}
……
}

可以看到,Arrays.asList的形参是可变参数T... a,等同于一个数组参数T[]。这里的T是泛型。在调用该api时,会直接用传入的参数来构建一个ArrayList。

这个ArrayList<E>是Arrays的静态内部类,同样使用了泛型,而泛型是不支持基础数据类型的。

当传入的参数是一个基础数据类型的数组时,就把整个数组对象解析为泛型T;如果传入的参数是一个对象类型的数组,就把数组中的对象类型解析为泛型T。如下:

传入的参数是int[]时:
int[] -> T[]中的T,此时Arrays.asList()返回的是一个size为1的ArrayList<int[]> 传入的参数是Integer[]时:
Integer[] -> T[],此时Arrays.asList()返回的是一个ArrayList<Integer>,其size的值与Integer[]的length一样

因此,题目里的list1和list2的size会不一样。那为什么直接传入1,2,3,4这四个int参数所得到的结果又是4呢?

这是因为当直接传入参数为基础数据类型时,由于方法形参是泛型数组,于是就通过自动装箱把基础数据类型的参数包装为对应的包装类。比如传入的是int,就自动装箱成Integer,这样就能被泛型所接收了。

也就是说,虽然传入参数是1,2,3,4,其实会通过自动装箱变成一个Integer[]参数,然后传递给T[],最后返回的就是一个ArrayList<Integer>

下面是一个可以证明该过程的例子:

public static void main(final String[] args) {
final int[] array1 = new int[]{1,2,3,4};
final Integer[] array2 = new Integer[]{1,2,3,4}; test(array1);
test(array2);
test(1,2,3,4);
} public static <T> void test(final T... a) {
System.out.println(a.length);
}

其结果如下:

1
4
4

Arrays.asList的其他知识点

由于Arrays.asList返回的是Arrays的静态内部类ArrayList,这个ArrayList并没有重写add和remove方法的。也就是说,这个ArrayList一旦new出来了,其大小就固定下来了,不能再调用add或者remove方法了,否则就会报错如下:

Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.AbstractList.add(AbstractList.java:148)
at java.util.AbstractList.add(AbstractList.java:108)

虽然不能调用add或者remove,但可以调用set、contains、sort等其他的方法,也可以进行遍历。

如果我们确实需要调用add或者remove方法,可以有以下方法:

方法一

遍历Arrays.asList返回的集合,然后一个个添加到我们常用的集合里,比如java.util.ArrayList

方法二

使用list.addAll(Arrays.asList(a)),直接把Arrays.asList返回的集合给整个添加到新的集合里。

方法三

可以直接通过new ArrayList<>(Arrays.asList(a))的方法来构建一个有着完善功能的集合。

方法四

使用Collections.addAll()来替代Arrays.asList(),这样得到的就是一个有着完善功能的集合。

泛型(Generics)的知识点

泛型的定义:在程序中我们将一个对象放入集合中,但是集合不会记住对象的类型,当我们在次使用对象的时候,对象变为Object类型,而程序中还是原来的类型,我们必须要自己转换其类型,为了解决这个问题,则提出泛型。

泛型要求包容的是对象类型,而八种基础数据类型不属于对象类型,但是它们有对应的封装类/包装类。并且在调用函数时,会根据参数类型来进行自动装箱或者自动拆箱(Autoboxing and unboxing)。对自动装箱/拆箱有兴趣的可以参考下边的链接。

参考链接

Java - 一道关于Arrays.asList的题目的更多相关文章

  1. Java中关于Arrays.asList()的操作

    我们可以通过Arrays.asList() 产生一个List,但是要记住,我们通过Arrays.asList产生的list是基于一个固定大小的数组的, 仅支持那些不会改变数组大小的操作.所以我们在使用 ...

  2. Java中关于Arrays.asList方法的深入学习与理解

    Java的标准库中在java.util包下提供了很多实用的工具类,如:Arrays,Collections等工具类都提供了一些比较实用的方法.在实际的开发使用中,我们经常需要使用这样的需求:将一个数组 ...

  3. 为什么Java里的Arrays.asList不能用add和remove方法?

    在平时的开发过程中,我们知道能够将一个Array的对象转化为List.这种操作,我们仅仅要採用Arrays.asList这种方法即可了.笔者前段时间一直用这种方法,有一天,我发现通过Arrays.as ...

  4. 【Java 基础】Arrays.asList、ArrayList的subList注意事项

    1. 使用Arrays.asList的注意事项 1.1 可能会踩的坑 先来看下Arrays.asList的使用: List<Integer> statusList = Arrays.asL ...

  5. Arrays.asList 为什么不能 add 或者 remove 而 ArrayList 可以

    分析如下例子: 1 import java.util.Arrays; 2 import java.util.List; 3 4 5 public class Test { 6 public stati ...

  6. 关于 ArrayList.toArray() 和 Arrays.asList().toArray()方法

    1.ArrayList.toArray() 理解    * 通过源码我们可以看到返回的是Object类型的数组,失去了原有的实际类型,虽然底层存储是具体类型的对象,这也正体现了文档中说的:该方法起到了 ...

  7. Arrays.asList()使用指南

    简介 Arrays.asList()在平时开发中还是比较常见的,我们可以使用它将一个数组转换为一个List集合. String[] myArray = { "Apple", &qu ...

  8. 【转】java.util.Arrays.asList 的用法

    DK 1.4对java.util.Arrays.asList的定义,函数参数是Object[].所以,在1.4中asList()并不支持基本类型的数组作参数. JDK 1.5中,java.util.A ...

  9. java Arrays.asList()和Collections.addAll()

    java中的方法Arrays.asList(arg1,arg2,arg3...),经常用在将多个元素或数组转化为List中的元素,但是在使用的时候,应该注意: arg1决定返回list的元素类型(即第 ...

随机推荐

  1. 【LeetCode】两个有序数组合成一个有序数组

    a = [1,3,5,7,9]b = [2,4,6,8,10]c= [] while len(a) > 0: if len(b) == 0: c.extend(a) break min_num ...

  2. listen 73

    Give Time to Feel Less Time-Squeeze Meetings, calls, kids, dogs, errands, exercise—and all those ema ...

  3. listen 64

    Winning Athletes Engage in Arms Raise You see it when you watch almost any game: there's a touchdown ...

  4. ACM学习历程—HDU 5317 RGCDQ (数论)

    Problem Description Mr. Hdu is interested in Greatest Common Divisor (GCD). He wants to find more an ...

  5. ACM学习历程—Hihocoder 1164 随机斐波那契(数学递推)

    时间限制:5000ms 单点时限:1000ms 内存限制:256MB 描述 大家对斐波那契数列想必都很熟悉: a0 = 1, a1 = 1, ai = ai-1 + ai-2,(i > 1). ...

  6. 自动拆装箱(int,Integer)

    包装类型Java语言是一个面向对象的语言,但是Java中的基本数据类型却是不面向对象的,这在实际使用时存在很多的不便,为了解决这个不足,在设计类时为每个基本数据类型设计了一个对应的类进行代表,这样八个 ...

  7. drop asm disk、撤销drop asm disk

    drop asm disk.撤销drop asm disk drop asm disk:SQL> alter diskgroup XXX offline disk XXXX drop after ...

  8. CF1076E:Vasya and a Tree

    浅谈树状数组与线段树:https://www.cnblogs.com/AKMer/p/9946944.html 题目传送门:https://codeforces.com/problemset/prob ...

  9. 使用二次封装的openStack发行版本网卡至少有2个

  10. stm32之开发入门

    一.开发环境配置 在开发stm32应用之前,我们需要先配置好开发环境. 首先从keil官网下载keil MDK-ARM软件包(v5版本与v4版本不同,v5版本需要下载额外的stm32芯片包)和芯片包( ...