List列表的坑

本文揭示了使用Java中Arrays.asList方法转换数组为List时常见的三个误区,包括无法直接转换基本类型数组、返回的List不支持增删操作及原始数组修改的影响,并提供了解决方案。

List列表的坑

Arrays.asList把数据转换为List的3个坑:

不能直接使用 Arrays.asList 来转换基本类型数组。 Arrays.asList方法返回的元素类型为泛型,直接遍历会报错,需要使用Arrays.stream方法来转换,或者把int数组声明为包装类型Integer数组

Arrays.asList 返回的 List 不支持增删操作。Arrays.asList 返回的 List 并不是我们期望的 java.util.ArrayList,而是 Arrays 的内部类 ArrayList。ArrayList 内部类继承自 AbstractList 类,并没有覆写父类的 add 方法,而父类中 add 方法的实现,就是抛出 UnsupportedOperationException。

对原始数组的修改会影响到我们获得的那个 List。解决方法:

String[] arr = {"1", "2", "3"};
List list = new ArrayList(Arrays.asList(arr));

一定要让合适的数据结构做合适的事情

使用数据结构需要平衡时间和空间。

要对大 List 进行单值搜索的话,可以考虑使用 HashMap,其中 Key 是要搜索的值,Value 是原始对象,会比使用 ArrayList 有非常明显的性能优势。

如果要对大 ArrayList 进行去重操作,也不建议使用 contains 方法,而是可以考虑使用 HashSet 进行去重。

使用 ObjectSizeCalculator 工具打印 ArrayList 和 HashMap 的内存占用,可以看到 ArrayList 占用内存 21M,而 HashMap 占用的内存达到了 72M,是 List 的三倍多。进一步使用 MAT 工具分析堆可以再次证明,ArrayList 在内存占用上性价比很高,77% 是实际的数据(如第 1 个图所示,16000000/20861992),而 HashMap 的“含金量”只有 22%(如第 2 个图所示,16000000/72386640)

数据结构中的大O时间复杂度并不一定在现实情况中适用

因为对于插入操作,LinkedList 的时间复杂度其实也是 O(n)。因为在实现的时候,我们需要先通过循环获取到那个节点的 Node,然后再执行插入操作。前者也是有开销的,不可能只考虑插入操作本身的代价。

继续做更多实验的话你会发现,在各种常用场景下,LinkedList 几乎都不能在性能上胜出 ArrayList。

这告诉我们,任何东西理论上和实际上是有差距的,请勿迷信教科书的理论,最好在下定论之前实际测试一下。抛开算法层面不谈,由于 CPU 缓存、内存连续性等问题,链表这种数据结构的实现方式对性能并不友好,即使在它最擅长的场景都不一定可以发挥威力。

<think> 嗯,用户现在明确要了解JavaList接口自带的方法。虽然引用了两篇站内资料,但第一篇主要讲集合分类,第二篇讲空值处理,都不是直接对应需求。需要基于Java标准库的List接口文档来组织信息。 用户可能是Java初学者或需要查阅API细节的开发人员。考虑到List是集合框架最常用的接口之一,应该按功能维度分类讲解核心方法,避免直接堆砌所有35个方法造成记忆负担。 先从基础操作入手比较合理:增删改查是任何数据结构的核心。ArrayList和LinkedList的底层实现差异会影响方法性能,这点必须作为补充说明,例如add(0,element)在LinkedList是O(1)而在ArrayList是O(n)。 注意到用户上次询问过空值处理(引用[2]),虽然本次未提及,但removeIf()方法配合Objects::isNull能优雅处理null值,这个实用技巧值得补充。另外线程安全方面需要明确说明Collections.synchronizedList的封装必要性——直接调用原生List方法在多线程环境会出问题。 对于Java8新增方法如replaceAll()和sort()要多给示例,因为lambda表达式不是所有人都熟悉。比如list.replaceAll(x -> x*2)比传统for循环简洁得多。 最后考虑加入版本适配提示:subList()返回的视图在原始列表修改时可能失效,这个很多开发者都踩过。Collections.unmodifiableList()虽然不在List接口内,但属于常用配套方案,应该作为拓展建议。 相关问题的设计要覆盖应用场景(排序/去重)、性能对比(ArrayList vs LinkedList)、特殊处理(不可变列表)。用户如果问"自带方法",大概率后续会问比较或第三方库方案,所以提前准备Guava和Apache Commons的延伸方向。 </think> 以下是Java中`List`接口核心方法的分类解析,结合官方文档与实际开发场景: ### 一、基础操作方法 1. **元素操作** - `add(E e)`:尾部添加元素(返回`boolean`) - `add(int index, E e)`:指定位置插入元素 - `remove(int index)`:移除指定位置元素(返回被删元素) - `remove(Object o)`:移除首个匹配对象(返回`boolean`) - `set(int index, E e)`:替换指定位置元素(返回旧元素) - `get(int index)`:获取指定位置元素 ```java List<String> list = new ArrayList<>(); list.add("A"); // [A] list.add(0, "B"); // [B, A] String old = list.set(1, "C"); // [B, C] 返回"A" ``` 2. **批量操作** - `addAll(Collection c)`:添加整个集合 - `removeAll(Collection c)`:移除所有匹配元素 - `retainAll(Collection c)`:仅保留指定集合中的元素 - `clear()`:清空列表 ### 二、查询与判断方法 1. **状态检查** - `size()`:元素数量 - `isEmpty()`:是否为空列表 - `contains(Object o)`:是否包含某元素 - `containsAll(Collection c)`:是否包含指定集合所有元素 2. **位置查找** - `indexOf(Object o)`:返回首个匹配元素的索引(无则返回-1) - `lastIndexOf(Object o)`:返回最后一个匹配元素的索引 ### 三、进阶操作(Java 8+) 1. **条件删除** ```java list.removeIf(e -> e.contains("X")); // 删除包含"X"的元素 ``` 2. **批量替换** ```java list.replaceAll(String::toUpperCase); // 所有元素转大写 ``` 3. **排序** ```java list.sort(Comparator.naturalOrder()); // 自然顺序排序 ``` ### 四、视图与迭代 1. **子列表视图** ```java List<String> sub = list.subList(1, 3); // 截取索引[1,3)的视图 sub.clear(); // 原list对应元素被删除 ``` 2. **迭代器** ```java Iterator<String> it = list.iterator(); while(it.hasNext()) { if(it.next().equals("target")) it.remove(); // 安全删除 } ``` ### 五、重要实现类对比 | 方法 | `ArrayList` | `LinkedList` | |----------------------|-------------------|-------------------| | `get(int index)` | $O(1)$ 高速随机访问 | $O(n)$ 需遍历链表 | | `add(int index, E e)`| $O(n)$ 需移动元素 | $O(1)$ 修改指针 | | `remove(int index)` | $O(n)$ | $O(1)$ | > **注意**: > - 使用`subList()`返回的视图会直接影响原列表[^1] > - 迭代中修改列表必须通过`Iterator`的`remove()`避免并发异常 > - 需处理`null`值时可用`list.removeIf(Objects::isNull)`[^2]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值