Java 开发者须知:Arrays.asList() 的五大陷阱

Arrays.asList() 都有什么隐藏的陷阱

Arrays.asList() 方法我们平时开发中一定经常使用,它是将数组转换为List的一种便捷方式,但它有一些潜在的陷阱需要注意。使用的时候需要多多注意呦。

不可变性

Arrays.asList() 返回的List是固定大小的,这意味着它不支持对元素的增删操作。任何试图修改大小的操作都会导致UnsupportedOperationException
因为它返回的是 java.util.Arrays.ArrayList 的实例,而不是 java.util.ArrayListArrays.ArrayList 是一个内部类,它基于一个固定大小的数组,并将其包装为 List 接口的实现。因此,它不支持添加或删除元素,只能对现有元素进行修改。
源码贴张图吧,能看到size属性是按照入参的长度给定的:
在这里插入图片描述
示例

import java.util.Arrays;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        // 创建一个固定大小的List
        List<String> list = Arrays.asList("a", "b", "c");
        
        // 尝试添加元素
        // list.add("d"); // 这会抛出 UnsupportedOperationException
        
        // 尝试删除元素
        // list.remove(0); // 这会抛出 UnsupportedOperationException
        
        // 尝试清空列表
        // list.clear(); // 这会抛出 UnsupportedOperationException
        
        // 可以修改现有元素
        list.set(0, "A");
        
        System.out.println(list); // 输出 [A, b, c]
    }
}

返回的是java.util.Arrays$ArrayList

返回的列表是 java.util.Arrays 的一个私有静态类,而不是标准的 ArrayList 类。这意味着一些 ArrayList 的方法和特性可能不可用。
Arrays.asList() 返回的 ArrayList 和真正的 ArrayList 在某些方面是相似的,但也有一些重要的区别。

相似之处:

  1. 元素访问:两者都支持通过索引访问元素。
  2. 迭代:可以使用迭代器或增强型 for 循环来遍历它们。
  3. 尺寸:都具有 size() 方法来获取列表的大小。

区别:

1. 可修改性:

  • Arrays.asList() 返回的列表是固定大小的,不能添加或删除元素,也不能调整大小。
  • 真正的 ArrayList 具有添加、删除和调整大小的方法,因此它是可修改的。

2. 添加/删除元素方法:

  • Arrays.asList() 返回的列表不支持添加或删除元素的方法(例如 add()remove() 等),尝试调用这些方法将会抛出 UnsupportedOperationException 异常。
  • 真正的 ArrayList 具有添加、删除和替换元素的方法。

3. 修改元素:

  • Arrays.asList() 返回的列表允许修改元素,但不允许改变列表的大小。
  • 真正的 ArrayList 则允许修改元素,并且也可以添加或删除元素。

4. toArray() 方法:

  • Arrays.asList() 返回的列表的 toArray() 方法返回的数组是底层数组的视图,对该数组的修改将反映在列表中。
  • 真正的 ArrayListtoArray() 方法则返回一个全新的数组副本,对该数组的修改不会影响到列表。

5. clear() 方法:

  • 真正的 ArrayList 有一个 clear() 方法,用于清空列表中的所有元素,而 Arrays.asList() 返回的列表不支持此方法。

数组元素类型必须是引用类型

Arrays.asList() 方法的参数是可变参数(varargs),并且泛型参数类型被推断为数组的类型。因此,如果传递的是基本数据类型的数组,它将被看作是一个对象,而不是数组。因为它的签名是 asList(T... a),其中 T 是泛型类型。这意味着我们可以传递零个或多个参数给这个方法,而不需要显式地创建一个数组来传递。

对于参数类型的推断,Java 中的自动装箱和拆箱会影响到这一点。自动装箱是指基本数据类型可以自动转换为对应的包装类型,而自动拆箱则是指包装类型可以自动转换为基本数据类型。因此,如果传递的是基本数据类型的数组,Java 会将其自动装箱为对应的包装类型的数组。

这也是为什么如果传递的是基本数据类型的数组,它将被看作是一个对象,而不是数组。这里的数组实际上是对象数组,而不是基本数据类型的数组。

示例:

int[] array = {1, 2, 3};
List<int[]> list = Arrays.asList(array); // 注意这里的泛型参数是 int[]
System.out.println(list.size()); // 输出 1,因为传递了一个对象,即整个数组作为一个元素

相反,如果传递的是包装类型的数组,它将会被识别为数组的数组元素:

Integer[] array = {1, 2, 3};
List<Integer> list = Arrays.asList(array); // 注意这里的泛型参数是 Integer
System.out.println(list.size()); // 输出 3,因为传递了三个元素,即数组的每个元素都是列表的一个元素

数组内容修改影响List

因为Arrays.asList() 返回的List是基于原始数组的,如果对原始数组进行修改,会影响到返回的List,反之亦然。

示例:

String[] array = {"one", "two", "three"};
List<String> list = Arrays.asList(array);
System.out.println(list);//输出 [one, two, three]
array[0] = "four";
System.out.println(list); // 输出 [four, two, three]
list.set(1, "five");
System.out.println(Arrays.toString(array)); // 输出 [four, five, three]

在这里插入图片描述

使用ArrayList构造新的可变List

如果需要一个可变的List,最好使用new ArrayList<>(Arrays.asList(array)),这样就可以避免固定大小和不可变性的问题。

示例:

String[] array = {"one", "two", "three"};
List<String> list = new ArrayList<>(Arrays.asList(array));
list.add("four"); // 不会抛出 UnsupportedOperationException

总体而言,使用Arrays.asList()时,要注意其返回的List的特性,以避免潜在的问题。在需要可变性、对List进行增删操作或处理包含null元素的数组时,建议使用new ArrayList<>(Arrays.asList(array))等方式。

总结:

不可变性

  • 固定大小: Arrays.asList() 返回的 List 实际上是一个基于固定大小数组的 java.util.Arrays.ArrayList 实现。这意味着该列表的大小是固定的,你无法添加或删除元素。
  • 尝试修改: 如果你尝试对这样的列表执行添加、删除或清空等操作,将会抛出 UnsupportedOperationException
  • 修改元素: 虽然不能改变列表的大小,但是可以修改列表中的元素。

返回的是 java.util.Arrays$ArrayList

  • 实现细节: 返回的列表是一个 java.util.Arrays 类的内部类 java.util.Arrays.ArrayList 的实例,而不是 java.util.ArrayList
  • 相似之处: 支持索引访问、迭代以及 size() 方法。
  • 区别:
    • 可修改性: Arrays.asList() 返回的列表是只读的,不能修改大小;而真正的 ArrayList 是可变的。
    • 添加/删除元素: Arrays.asList() 返回的列表不支持添加或删除元素的方法,如 add()remove();真正的 ArrayList 支持这些操作。
    • 修改元素: 两种列表都允许修改现有元素,但 Arrays.asList() 不支持改变列表大小。
    • toArray() 方法: Arrays.asList() 返回的列表的 toArray() 方法返回的是原始数组的视图,对返回数组的修改会影响到列表本身;而真正的 ArrayListtoArray() 方法返回的是数组的副本。
    • clear() 方法: 真正的 ArrayListclear() 方法,用于清空列表;而 Arrays.asList() 返回的列表没有此方法。

数组元素类型必须是引用类型

  • 基本类型与引用类型: 如果你传递的基本类型数组,那么整个数组会被视为一个对象而非数组元素;如果你传递的是引用类型数组,则数组的每个元素都会被视为列表中的一个元素。
  • 自动装箱的影响: Java 的自动装箱机制会影响到数组元素类型的推断。基本类型数组会自动装箱为相应的包装类型数组。

数组内容修改影响 List

  • 共享底层数据: Arrays.asList() 返回的列表和原始数组共享相同的底层数据。这意味着如果修改了数组的内容,列表也会随之改变;同样,如果修改了列表中的元素,数组的内容也会受到影响。

使用 ArrayList 构造新的可变 List

  • 解决方法: 如果你需要一个可变的列表,可以使用 new ArrayList<>(Arrays.asList(array)) 来创建一个新的 ArrayList,它会包含原始数组的内容,并且是可变的。

总之,在使用 Arrays.asList() 时,要特别注意其返回的列表的限制,特别是不可变性和与其他集合类的区别。如果需要一个可变的列表,最好采用上述建议的方法来创建新的列表实例。

来源:https://juejin.cn/post/7337147602851446810

  • 15
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值