Arrays.asList方法的几个坑

0. 前言

在将数组转换成 List 的时候,我们一般会使用 Arrays.asList 方法,但 Arrays.asList 方法有以下几个坑需要注意:

  1. 基本数据类型构成的数组转换为 List 时,List 中存的是数组
  2. 对原数组进行修改操作会影响到转换后的 List 对象
  3. 通过 Arrays.asList 得到的 List 对象不能进行添加元素或删除元素的操作

如果我们不太了解 Array.asList 方法,出现相关问题的时候可能需要花费不少时间来调试,所以了解 Arrays.asList 方法是很有必要的

1. 坑点一:基本数据类型构成的数组转换为List时,List中存的是数组

1.1 坑点复现

我们声明一个 int 类型的数组,通过 Arrays.asList 方法将数组转换为 List,接着输出 List 的长度

在这里插入图片描述

@Test
public void testBasicDataTypeArray() {
    
    
    int[] intArray = {
    
    1, 2, 3};
    List<?> intList = Arrays.asList(intArray);
    
    System.out.println("intList.size() = " + intList.size());
}

输出结果如下

在这里插入图片描述

为什么 intList 中只有一个元素呢,原数组不是有三个元素吗

我们打印 List 中的元素和 List 中元素的类型,发现 List 中元素的类型竟是一个整型数组!!!

@Test
public void testBasicDataTypeArray() {
    
    
    int[] intArray = new int[]{
    
    1, 2, 3};
    List<?> intList = Arrays.asList(intArray);
    
    System.out.println("intList.size() = " + intList.size());
    System.out.println("intList = " + intList);
    System.out.println("intList.get(0).getClass() = " + intList.get(0).getClass());
}

在这里插入图片描述


在 Java 中,打印一个对象时,默认会调用该对象的toString()方法,对于数组对象,toString()方法返回的字符串包含以下部分:

  • 数组的类名(例如[I表示一个整型数组)
  • @符号
  • 数组对象的哈希码(例如55a1c291

为什么 List 中只有一个元素,而且元素类型为整型数组呢,我们查看 Arrays.asList 方法的源码,发现 asList 方法接收的参数是泛型

在这里插入图片描述

如果参数是泛型的话,那么传入的参数必须是 直接或间接地继承了 Object 类 的类,但是基本数据类型并没有继承 Object 类

然而,数组的本质也是一个对象,也就是引用数据类型,数组隐式地继承了 Object 类

在上述例子中,intArray 数组会作为一个对象传给 asList 方法,返回的 List 中存储的就是 intArray 数组


其实,如果你使用了 IDEA 的自动填写方法返回值功能,就能快速发现这个问题

IDEA 自动填写方法返回值的两种方式:

  • 方法调用.var + 回车键
  • 快捷键:CTRL + ALT + V

以下是 方法调用.var + 回车键 自动填写方法返回值的方式

在这里插入图片描述

在这里插入图片描述

1.2 解决方案

1.2.1 方案一:将基本数据类型改为基本数据类型的包装类

@Test
public void testBasicDataTypeArray() {
    
    
    Integer[] intArray = {
    
    1, 2, 3};
    List<?> intList = Arrays.asList(intArray);
    
    System.out.println("intList.size() = " + intList.size());
    System.out.println("intList = " + intList);
    System.out.println("intList.get(0).getClass() = " + intList.get(0).getClass());
}

在这里插入图片描述

1.2.2 方案二:使用 stream 流转换

使用Arrays.stream()配合boxed()collect(Collectors.toList())

在这里插入图片描述

@Test
public void testBasicDataTypeArray() {
    
    
    int[] intArray = {
    
    1, 2, 3};
    List<Integer> intList = Arrays.stream(intArray).boxed().toList();
    
    System.out.println("intList.size() = " + intList.size());
    System.out.println("intList = " + intList);
    System.out.println("intList.get(0).getClass() = " + intList.get(0).getClass());
}

2. 坑点二:对原数组进行修改操作会影响转换后的List对象

2.1 坑点复现

将原数组转换为 List 之后,如果再对原数组进行修改操作,会影响转换后的 List 对象

在这里插入图片描述

@Test
public void testAffect() {
    
    
    Integer[] integerArray = {
    
    1, 2, 3};
    List<Integer> integerList = Arrays.asList(integerArray);
    integerArray[1] = -2;
    
    System.out.println("integerArray = " + Arrays.toString(integerArray));
    System.out.println("integerList.size() = " + integerList.size());
    System.out.println("integerList = " + integerList);
    System.out.println("integerList.get(0).getClass() = " + integerList.get(0).getClass());
}

输出结果如下

在这里插入图片描述

可以看到,对原数组进行修改操作,转换后的 List 对象也会受到影响,说明原始数组和转换后的 List 用的是同一个引用

在开发的过程中,如果将 Arrays.asList 方法得到的 List对象作为参数传递到另一个方法中,另一个方法对 List 进行了修改操作

如果不知道原数组会受到影响的话,出现相关问题的时候可能需要花费不少时间进行调试

2.2 解决方法:重新创建新的ArrayList对象

我们创建一个新的 ArrayList 对象,不使用原数组的引用,对原数组进行修改操作,也不会影响到 List 对象

在这里插入图片描述

@Test
public void testAffect() {
    
    
    Integer[] integerArray = {
    
    1, 2, 3};
    List<Integer> integerList = new ArrayList<>(Arrays.asList(integerArray));
    integerArray[1] = -2;
    
    System.out.println("integerArray = " + Arrays.toString(integerArray));
    System.out.println("integerList.size() = " + integerList.size());
    System.out.println("integerList = " + integerList);
    System.out.println("integerList.get(0).getClass() = " + integerList.get(0).getClass());
}

测试结果如下

在这里插入图片描述

2.3 补充:new ArrayList(Collection c)方法创建的List底层用的是浅拷贝

new ArrayList(Collection c)方法创建的 ArrayList 使用的是原始数组元素的引用副本,而不是元素的深拷贝

如果原始数组中的元素是不可变的基本类型(如 int 等基本数据类型),则没有问题,因为基本类型的值本身是直接存储的,而不是引用

但是,如果原始数组包含的是对象,那么新列表中的元素将是对原始数组中对象的引用,如果修改了原始数组中的对象,新列表中的对应对象也会被修改,因为它们共享相同的引用


以下是一个证明 new ArrayList(Collection c)方法创建的List底层用的是浅拷贝 的例子

在这里插入图片描述

输出结果如下

在这里插入图片描述

@Test
public void testDeepCopyOrShallowCopy() {
    
    
    Order firstOrder = new Order();
    firstOrder.setId(1L);
    firstOrder.setProductName("商品1");
    firstOrder.setProductDescription("商品描述1");
    firstOrder.setTotalAmount("100.00");

    Order secondOrder = new Order();
    secondOrder.setId(2L);
    secondOrder.setProductName("商品2");
    secondOrder.setProductDescription("商品描述2");
    secondOrder.setTotalAmount("200.00");

    Order[] orderArray = {
    
    firstOrder, secondOrder};
    List<Order> orderList = new ArrayList<>(Arrays.asList(orderArray));

    System.out.println("====================修改前====================");
    System.out.println("orders = " + Arrays.toString(orderArray));
    System.out.println("orderList = " + orderList);
    System.out.println("====================修改前====================");

    orderArray[0].setId(3L);
    orderArray[0].setProductName("商品3");
    orderArray[0].setProductDescription("商品描述3");
    orderArray[0].setTotalAmount("300.00");

    System.out.println("====================修改后====================");
    System.out.println("orders = " + Arrays.toString(orderArray));
    System.out.println("orderList = " + orderList);
    System.out.println("====================修改后====================");
}

Order.java

/**
 * 订单类
 */
public class Order {
    
    

    /**
     * 用于唯一标识一次支付请求,可以是订单号或与其他业务相关的唯一标识
     */
    private Long id;

    /**
     * 支付的总金额
     */
    private String totalAmount;

    /**
     * 支付时显示的商品描述
     */
    private String productDescription;

    /**
     * 支付时显示的商品名称
     */
    private String productName;

    public Long getId() {
    
    
        return id;
    }

    public void setId(Long id) {
    
    
        this.id = id;
    }

    public String getTotalAmount() {
    
    
        return totalAmount;
    }

    public void setTotalAmount(String totalAmount) {
    
    
        this.totalAmount = totalAmount;
    }

    public String getProductDescription() {
    
    
        return productDescription;
    }

    public void setProductDescription(String productDescription) {
    
    
        this.productDescription = productDescription;
    }

    public String getProductName() {
    
    
        return productName;
    }

    public void setProductName(String productName) {
    
    
        this.productName = productName;
    }

    @Override
    public String toString() {
    
    
        return "Order{" +
                "id=" + id +
                ", totalAmount='" + totalAmount + '\'' +
                ", productDescription='" + productDescription + '\'' +
                ", productName='" + productName + '\'' +
                '}';
    }

}

3. 坑点三:通过Arrays.asList得到的List对象不能进行添加元素或删除元素的操作

通过 Arrays.asList 得到的的 List 不能进行添加元素或删除元素的操作

3.1 坑点复现

在这里插入图片描述

@Test
public void testAddElement() {
    
    
    Integer[] intArray = {
    
    1, 2, 3};
    List<Integer> integerList = Arrays.asList(intArray);
    integerList.add(4);
    
    System.out.println("integerList.size() = " + integerList.size());
    System.out.println("integerList = " + integerList);
    System.out.println("integerList.get(0).getClass() = " + integerList.get(0).getClass());
}

运行程序后报出以下错误

在这里插入图片描述

为什么会抛出 UnsupportedOperationException 异常呢,我们查看一下 Array.asList 方法的源码

在这里插入图片描述

可以发现,asList 方法返回的是一个 ArrayList 对象,但是这个 ArrayList 不是我们日常使用的 ArrayList,而是 Arrays 类里面的一个私有内部类,该内部类继承了 AbstractList 类

AbstractList 的 add 方法并没有具体的实现,调用 AbstractList 的 add 方法会抛出 UnsupportedOperationException 异常(remove 方法也一样)

在这里插入图片描述

而我们日常使用的 ArrayList 是在 java.util 包下的,java.util 包下的 ArrayList 已经重写了 add 方法和 remove 方法

在这里插入图片描述

3.2 解决方案:重新创建一个java.util包下的ArrayList对象

与第二个坑点的解决方案类似,我们不使用 asList 方法返回的 Arrays 类内部的 ArrayList 对象,而是利用返回的 ArrayList 对象重新创建一个 java.util 包下的 ArrayList 对象

在这里插入图片描述

@Test
public void testAddElement() {
    
    
    Integer[] integerArray = {
    
    1, 2, 3};
    List<Integer> integerList = new ArrayList<>(Arrays.asList(integerArray));
    integerArray[1] = -2;
    integerList.add(4);

    System.out.println("integerArray = " + Arrays.toString(integerArray));
    System.out.println("integerList.size() = " + integerList.size());
    System.out.println("integerList = " + integerList);
    System.out.println("integerList.get(0).getClass() = " + integerList.get(0).getClass());
}

运行结果如下

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/m0_62128476/article/details/142922229