我是靠谱客的博主 认真萝莉,这篇文章主要介绍探索List和Map循环遍历删除问题前言问题引入问题分析扩展到Map结语,现在分享给大家,希望可以做个参考。

通过源码解读Java中List和Map循环遍历导致的删除问题。

前言

Java代码写的其实不多,上周写List和Map的遍历,需要删除里面的元素时,直接就抛出异常,因为接触Java时间并不长,这种方式之前也很少使用,所以感觉这里肯定有坑,然后Java对List和Map的遍历方式也是五花八门,今天想花点时间研究了一下。

问题引入

我们先看List的4种遍历情况,你看哪种会有问题:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
List<String> platformList = new ArrayList<>(); platformList.add("掘金"); platformList.add("知乎"); platformList.add("微信公账号"); // List情况1: for (String platform : platformList) { if (platform.equals("掘金")) { platformList.remove(platform); } } System.out.println(platformList); // List情况2: platformList.forEach(platform -> { if (platform.equals("知乎")) { platformList.remove(platform); } }); System.out.println(platformList); // List情况3: Iterator iterator3 = platformList.iterator(); while (iterator3.hasNext()) { String platformStr = (String) iterator3.next(); if (platformStr.equals("掘金")) { platformList.remove(platformStr); } } System.out.println(platformList); // List情况4: Iterator<String> iterator4 = platformList.iterator(); while (iterator4.hasNext()) { String platformStr = iterator4.next(); if (platformStr.equals("掘金")) { iterator4.remove(); } } System.out.println(platformList); 复制代码

我想很多同学都知道,情况1、2、3都会抛出异常,情况4是没有问题的。

如果我再多问一句,为什么会这样呢?

问题分析

异常使用

我们以“List情况1”为例,结合源码分析一下。其实“List情况1”和“List情况2”都是一样的,执行循环过程中,其实是使用的Iterator,使用的核心方法是hasnext()和next(),所以“List情况1”和“List情况2”,可以转换成“List情况3”的方式:

复制代码
1
2
3
4
5
6
7
8
9
10
// List情况3: Iterator iterator3 = platformList.iterator(); while (iterator3.hasNext()) { String platformStr = (String) iterator3.next(); if (platformStr.equals("掘金")) { platformList.remove(platformStr); } } System.out.println(platformList); 复制代码

下面我们看一下“List情况1”的执行过程:

整个流程非常清晰,其实就是因为List中维护了一个变量expectedModCount,该变量是List的初始大小,当你新增或者删除元素时,会修改临时变量modCount的值,每次循环时,Java会判断两个值是否相等,如果不相等,就会抛出异常。

正常使用

那为什么“List情况4“就没问题呢?我们看一下“List情况4“的执行过程:

第1步:

第2步:

第3步:

第4步:

第5步:

为了方便说明,我把步骤标明了一下,我们发现第1、2、4步执行的方法和前面“异常使用”的流程是一模一样的,但是第3步中有个remove()方法,这个其实是Iterator的remove()方法,然后第4步remove()方法其实是List的remove()方法,两个是嵌套关系。当执行完List的remove()方法后,Iterator的remove()方法会执行“第5步”的“expectedModCount = modCount”,重新让两者相等,这就是使用迭代器删除数据,不会抛出异常的原因所在。

扩展到Map

其实Map和List基本差不多,我们看看Map的使用情况:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
Map<Integer, String> platformMap = new HashMap<>(); platformMap.put(1, "掘金"); platformMap.put(2, "知乎"); platformMap.put(3, "微信公账号"); // Map情况1: for (Map.Entry<Integer, String> entry : platformMap.entrySet()) { Integer entryKey = entry.getKey(); if (entryKey.equals(1)) { platformMap.remove(1); } } System.out.println(platformMap); // Map情况2: platformMap.forEach((key, value) -> { if (key.equals(1)) { platformMap.remove(1); } }); System.out.println(platformMap); // Map情况3: Iterator<Integer> iterator = platformMap.keySet().iterator(); while (iterator.hasNext()) { Integer platformMapKey = iterator.next(); if (platformMapKey.equals(1)) { iterator.remove(); } } System.out.println(platformMap); 复制代码

直接给出结论:“Map情况1”和“Map情况2”属于异常使用,“Map情况3”属于正常使用。

我们直接看看Map的迭代器执行remove()时的代码:

看到没,迭代器的remove()同样有强制相等的代码,然后removeNode()方法属于Map的成员方法,和List的设计方式一模一样。

结语

文章简单探索了Java中List和Map循环遍历导致的删除问题,如果需要避免踩坑,可以使用迭代器,或者也可以通过for循环,然后采用指针位移的方式,这个就有点类似于C++的数组遍历中删除的方式。

然后网上也有说通过removeIf()方法来代替Iterator的remove(),这个大家可以试一下。


作者:楼仔
链接:https://juejin.cn/post/7031775136692994079
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

最后

以上就是认真萝莉最近收集整理的关于探索List和Map循环遍历删除问题前言问题引入问题分析扩展到Map结语的全部内容,更多相关探索List和Map循环遍历删除问题前言问题引入问题分析扩展到Map结语内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(66)

评论列表共有 0 条评论

立即
投稿
返回
顶部