在各种Java编程规范中都强调尽量不用java.util.Arrays.asList()方法以避免调用此方法生成的List无法进行修改操作,个人近期在使用过程中发现该方法的另外一个坑,简单记录下。

问题描述

在程序中需要检测某个元素是否在数组中,处于缩短代码篇幅的考虑,自己采用了Arrays.asList方法将其转化为List然后调用contains方法来判断:

1
2
3
4
public static boolean checkContains(int ele) {
    int[] data = {11, 12, 13, 14};
    return Arrays.asList(data).contains(ele);
}

但实际执行结果和自己预期的不一致,理论上的结果应该为true,而实际返回的结果为false

Arrays.asList输出结果不符合预期

问题分析

在Debug模式下查看Arrays.asList(data)返回的值,发现其返回的结果不是预期中的List<Integer>而是List<int[]>,从而导致调用List.contains()方法失效!

Arrays.asList输出不符合预期的debug

在IDEA中将Arrays.asList(data)返回的结果单独赋值给一个变量,可发现其类型确实为List<int[]>,若将结果类型修改为List<Integer>则会报错,如下图所示:

修改代码将转化结果抽取出来

在上图右边的报错信息中有如下说明信息:

no instance(s) of type variable(s) exist so that int[] conforms to Integer inference variable T has incompatible bounds: equality constraints: Integer lower bounds: int[]

上述文字的核心内容为Arrays.asList需要获取Object类型,而int是原生类型,但int[]符合要求,因此Arrays.asList()方法会将原始的int数组视作一个int[]对象进行处理,从而导致此结果1

解决方案

找到问题原因后修改起来也很容易,按照通常的编程习惯,只需要把数组的定义从int修改为Integer即可

1
2
3
4
5
public static boolean checkContains(int ele) {
    Integer[] data = {11, 12, 13, 14};
    List<Integer> dataList = Arrays.asList(data);
    return dataList.contains(ele);
}

执行结果符合预期

Arrays.asList输出结果符合预期


Update:

Stackoverflow上找到一个类似问题2,在其回答中提供了一些其它实现方案:

  • JDK8实现

    1
    2
    
    int[] ints = new int[] {1,2,3,4,5};
    List<Integer> list11 =Arrays.stream(ints).boxed().collect(Collectors.toList()); 
    
  • JDK16实现

    1
    2
    
    int[] ints = new int[] {1,2,3,4,5};
    Arrays.stream(ints).boxed().toList();
    

asList不可变原因分析

查看Arrays.asList返回结果中的List,发现其返回的是内部自己实现的ArrayList,但该ArrayList没有重写add方法。

Arrays.asList实现了自定义的ArrayList

AbstractList默认没有实现add()方法而是抛出一个异常,从而导致无法通过调用add()方法添加元素。

AbstractList默认不支持add操作