文章目录
0. 前言
在将数组转换成 List 的时候,我们一般会使用 Arrays.asList 方法,但 Arrays.asList 方法有以下几个坑需要注意:
- 基本数据类型构成的数组转换为 List 时,List 中存的是数组
- 对原数组进行修改操作会影响到转换后的 List 对象
- 通过 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());
}
运行结果如下