2

阿里巴巴为什么这样强制从List中删除元素 - 程序员xiaozhang

 1 year ago
source link: https://www.cnblogs.com/scott1102/p/17280573.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

阿里巴巴为什么这样强制从List中删除元素

还是先举个例子,你侄女对天文知识感兴趣,然后你就用程序写了太阳系九大星系(水星、金星、地球、火星、木星、土星、天王星、海王星、冥王星)的运行轨迹图,然后拿给侄女看。然后她说错了错了,你的知识太旧了,多了一颗星。根据2006年8月24日国际天文联合大会召开,在会议上经过投票表决,冥王星被降级为矮行星,太阳系目前只剩下八颗行星。所以你需要删除一颗星。这个时候你打开电脑开始删除冥王星。

2591839-20230402151149459-1210465594.jpg

你从下面List中删除一颗星。

List<String> tempList = Arrays.asList("水星","金星","地球","火星",
"木星","土星","天王星","海王星","冥王星","冥王星");

怎么删除List中的冥王星呢?[PS为了演示某些删除方法不可靠,重复写了冥王星] 。

先写一段阿里规约:

【强制】不要再foreach循环里进行元素的remove/add操作,remove元素请使用Iterator方式,如果并发的操作,需要对Iterator对象加锁。

好了,那就让我们来写所有可能删除元素的方法

2591839-20230402151404114-1843944202.jpg

1:普通的for循环的删除(不可靠)。

List<String> list = new ArrayList(tempList);
         for (int i = 0; i < list.size(); i++) {
            String str = list.get(i);
            if ("冥王星".equals(str)) {
                list.remove(i);
            }
        }
        System.out.println(list);

运行结果如下:

[水星, 金星, 地球, 火星, 木星, 土星, 天王星,    海王星, 冥王星]

奇了怪了,没删除干净?

问题出在 list.size(),因为 list.size() 和 i 都是动态变化的,i 的值一直在累加,list.size() 一直在减少,所以 list 就会早早结束了循环。所以这种方式虽然不会报错,但存在隐患,并且不容易被察觉,不建议使用。

2:普通的for循环提取变量进行删除(这个更不可靠,会报错)。

List<String> list = new ArrayList(tempList);
        int size = list.size();
        for (int i = 0; i < size; i++) {
            String result = list.get(i);
            if ("冥王星".equals(result)) {
                list.remove(i);
            }
        }
        System.out.println(list);

结果如下:

2591839-20230402151656090-1421055389.png

这更不对了,一下子搞出个下标越界。

因为 size 变量是固定的,但 list 的实际大小是不断减小的,而 i 的大小是不断累加的,一旦 i >= list 的实际大小肯定就异常了。

3:普通的for循环倒叙删除(这个用法可以,但也不推荐,倒叙看着很别扭,个人意见)。

for (int i = list.size() - 1; i > 0; i--) {
            String result = list.get(i);
            if ("冥王星".equals(result)) {
                list.remove(i);
            }
        }
        System.out.println(list);

运行结果如下:

[水星, 金星, 地球, 火星, 木星, 土星, 天王星, 海王星]

4:使用增强的for循环删除(会抛出异常,不推荐,注意我这次为了演示效果,把行星的顺序换一下),不少开发者喜欢用这种方式。

List<String> tempList = Arrays.asList("水星","金星","地球","火星",
"冥王星","土星","天王星","海王星","冥王星","木星");
         List<String> list = new ArrayList(tempList);
        for (String item : list) {
            if ("冥王星".equals(item)) {
                list.remove(item);
            }
        }
        System.out.println(list);

结果如下:

2591839-20230402152007720-707166781.png

奇了怪了,又抛异常了。不过这次的异常和上面的下标异常不太一样,这次是:

java.util.ConcurrentModificationException

这个是集合操作中很常见的异常之一,即并发修改异常!

增强的 for循环,其内部是调用的 Iterator 的方法,取下个元素的时候都会去判断要修改的数量(modCount)和期待修改的数量(expectedModCount)是否一致,不一致则会报错,而 ArrayList 中的 remove 方法并没有同步期待修改的数量(expectedModCount)值,所以会抛异常了。

5、迭代器循环迭代器删除(可靠,也是十分推荐的用法)。

Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()){
            String item = iterator.next() ;
            if ("冥王星".equals(item)){
                iterator.remove();
            }
        }
        System.out.println(list);

结果如下,十分完美和正确:

[水星, 金星, 地球, 火星, 土星, 天王星, 海王星, 木星]

这是因为迭代器中的 remove 方法将期待修改的数量(expectedModCount)值进行了同步。

 
2591839-20230402152258265-84250217.jpg

6:迭代器循环集合删除(这个可能很多开发者也会这样写,也可能会抛出异常的)。

Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()){
            String item = iterator.next() ;
            if ("冥王星".equals(item)){
                list.remove(item);
            }
        }
        System.out.println(list);

结果如下:

2591839-20230402152416530-122487047.png

7:Stream filter 过滤(十分推荐,当然使用这个删除需要JDK的环境在8及其8以上的版本)。

list = list.stream().filter(item -> !"冥王星".equals(item)).
 collect(Collectors.toList());
 System.out.println(list);

结果如下,十分完美和正确:

2591839-20230402152547548-539459461.png

这个方法利用了 Stream 的筛选功能,快速过滤所需要的元素,虽然不是进行集合删除,但达到了同样的目的,这种方法要更简洁

看了上面的几个例子,相信你熟悉了List删除元素的用法了,希望你看了上面的例子,开发的时候不会再犯错了。

2591839-20230402152633320-1474109957.jpg

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK