List大坑集「锦」

在日常工作中,当然我这里指的是 java 程序员工作,我们几乎所有的业务代码都离不开 List 集合,List 是我们经常使用的集合类之一,那么问题来了,你在使用 List 集合过程中有没有遇到过这样或那样的坑呢,今天就由我来分享下,我在工作中遇到的 List 集合的几个神坑。

首先在这里简单提及下 Array、 List、 ArrayList 的基本知识。

Array(数组)是基于索引(index)的数据结构,它使用索引在数组中搜索和读取数据是很快的,Array 获取数据的时间复杂度是 O(1)。但是要删除数据却是开销很大,因为这需要重排数组中的所有数据。可以利用 Arrays.asList 返回一个列表。

List 是一个有序的集合,可以包含重复的元素,提供了按索引访问的方式,它继承 Collection。注意!List 是一个接口。

ArrayList 类是继承 AbstractList 抽象类和实现List接口的一个实现类。由于 List 不能创建实例对象,我们可以利用 ArrayList 实现类的实例对象充当List接口的对象引用。另外 ArrayList 可以利用 toArray 方法返回一个数组。

下面开始数坑。

第一个坑:Arrays.asList 方法返回的 List 不支持增加、删除操作。

给各位一分钟看看下面这段代码有什么问题?

在这里插入图片描述

时间到!我就直说了。这段代码表面看起来没有任何问题,编译也能通过,但是真正测试运行的时候将会在第3行抛出如下异常:

为什么会这样呢?我们回顾下上面的知识, Arrays.asList 返回一个列表,要注意的是,这里的列表和 ArrayList 不是同一个哟,它只是 Arrays 的一个内部类。通过查看 java.util.ArrayList 和 java.util.Arrays A r r a y L i s t 的 类 图 发 现 a d d / r e m o v e 等 方 法 实 际 都 来 自 A b s t r a c t L i s t , 而 j a v a . u t i l . A r r a y s ArrayList 的类图发现 add/remove 等方法实际都来自 AbstractList,而 java.util.Arrays ArrayListadd/removeAbstractListjava.util.ArraysArrayList 并没有重写父类的方法。而父类方法恰恰都会抛出 UnsupportedOperationException。

第二个坑,Arrays.asList 方法返回的新 List 和该方法原始入参数组修改会相互影响。

再给各位一分钟快速看看这段代码,你猜输出结果是什么?

图片

不卖关子了,直接上结果。

图片

诶,integerList 的1,2呢?原来是 subList 直接使用的原始的数组,也就是说subList 和 integerList 共享了数组。这还得了,都是在共享数组了,那彼此在进行数据修改时极有可能会产生一些意想不到的 Bug。说到这里,那咱们该怎么避免这类 Bug 呢?标准答案来咯。

方法一将 Arrays.asList 作为 ArrayList 构造方法的参数重新 new 一个 List 即可。

方法二通过 Guava 库中的 Lists.newArrayList ,将返回的新 List 和原始的数组解耦,就不会再互相影响了。

第三个坑,直接遍历 List 集合删除元素会报错。

图片

上面的代码我们使用 foreach 方式遍历 List 集合,如果符合条件,将会从集合中删除改元素。这个程序编译正常,但是运行时,程序将会抛出如下异常:

图片

这是为什么呢?咱们查看其源码可以发现,删除元素方法 remove 会使集合结构发生修改,也就是 modCount( modCount来源于 ArrayList 的父类 AbstractList,可以用来记录 List 集合被修改的次数)会修改,在循环过程中,会比较当前 List 的集合实际修改的次数 modCount 与迭代器修改的次数 expectedModCount ,刚开始遍历循环的时候 expectedModCount==modCount, ArrayList#remove 之后将会使 modCount 加一, expectedModCount 与 modCount 将会不相等,这就导致迭代器遍历时将会抛 ConcurrentModificationException 异常。

解决方法主要有两种方式:

  • 使用 ArrayList 的迭代器方式遍历,然后调用其中 remove 方法。
  • 在 JDK 1.8+ 可以使用 removeIf 方法进行删除操作。

今天的分享就到这啦,欢迎各位友友前来交流指导,咱们互相学习,共同进步!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值