ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

日常开发中常遇到的一些问题和思考

2022-01-20 13:30:30  阅读:153  来源: 互联网

标签:删除 list equals remove add 日常 思考 淘宝 中常


一、常见的集合删除元素问题:使用for循环匹配某个元素,然后删除,猜猜下面代码输出的会是什么?

List<String> list = new ArrayList<>();
    list.add("淘宝");
    list.add("淘宝");
    list.add("掘金");
    for(String item : list) {
        if("淘宝".equals(item)) {
            list.remove(item);
        }
    }
System.out.println(list);

复制代码

解答: 输出【淘宝、掘金】,本意我们是想删除集合中为“淘宝”的元素,但是因为在循环中使用到list的remove方法,这样一旦有元素被移除后,集合的size会变小,这样原本的元素左边就会改变,最终循环提前退出,具体可看下面图纸解析:

解决方案:

方式1: 在调用remove方法后添加:i--,让下一轮坐标重新回到删除元素的前一个(因为这样删除会让被删除元素之后的元素左边都往前移一位)

方式2: 使用java8提供的removeIf API,完整代码:list.removeIf(e -> “淘宝”.equals(e)) 即可实现。

二、使用增强for循环删除元素,猜猜下面代码会输出什么?

List<String> list = new ArrayList<>();
    list.add("淘宝");
    list.add("淘宝");
    list.add("掘金");
    for(String item : list) {
        if("淘宝".equals(item)) {
            list.remove(item);
        }
    }
System.out.println(list);
复制代码

解答: 直接抛出ConcurrentModificationException异常,其实增强for循环只是一个语法糖,方便开发者使用,在虚拟机实际的被执行的时候是通过迭代器的方式循环,对上面的代码进行反编译得到如下代码:

List<String> list = new ArrayList();
    list.add("淘宝");
    list.add("淘宝");
    list.add("掘金");
    Iterator iterator = list.iterator()
    while(iterator .hasNext()) {
        String item = (String)iterator .next();
        if ("淘宝".equals(item)) {
            list.remove(item);
        }
    }
    System.out.println(list);
复制代码

为什么使用迭代器循环方式在循环的时候删除元素会抛出这个异常呢? 实际上,迭代器在每次调用next()方法的时候,第一步就会先去执行checkForComodification()方法,这个方法的目的就是检查list下的modCount是否 和expectedModCount变量的值一致(可以理解为版本号),如果不一致,则直接抛出ConcurrentModificationException异常。

删除的流程是这样的呢? 实际上modCount变量表示的含义是集合被修改的次数,每次对集合进行add或者remove的时候,则会对它进行+1,在调用集合的remove方法时,该变量的值会被加1,但是expectedModCount的值却没有变动,此时,当再次执行next方法时判断则两者值不相等,于是抛出异常。

解决方案:使用迭代器的remove方法,即将上面删除的代码修改为: iterator.remove(), 在迭代器的remove方法中,会将modCount的值赋值给expectedModCount,所以在下次执行next方法的时候,两者的值还是一样的,就不会抛出异常。

更优雅的解决方案: 使用java8中提供的removeIf方法,如:list.removeIf(e -> “淘宝”.equals(e)) ,其实这个方法内部也是使用了迭代器的方式进行删除。

了解完解决方案,我们再来深入研究下抛出异常的目的是为什么呢?

其实它是集合的一种保护机制,叫做“快速失败”,因为集合的remove操作都是非原子性的,在多线程情况下,可能出现一个线程在遍历的时候另外一个线程执行了删除操作,当集合的元素被删除后,集合的容量就会变小,在被删除的元素后面的元素坐标都会往前移动一位,这样循环迭代的时候就可能出现漏掉某些元素的情况,这种数据不同可能在当前场景下不会出现异常情况,但是会在某些代码环节出现异常情况,增加了排查的难度。

提供了“快速失败”机制后,如果在遍历的情况下有另外的线程来删除了元素,此时因为modCount和expectedModCount值不一致,则会抛出异常,迭代停止,这样我们就能够快速定位异常原因和位置。

三:Integer类型比较,猜猜下面返回什么结果

public static void test3(){
    Integer a = 127;
    Integer b = 127;
    Integer c = 128;
    Integer d = 128;
    
    System.out.println(a == b);
    System.out.println(c == d);
    System.out.println(c.equals(d));
}
复制代码

解答: 答案是true、false、true,你猜对了?

为什么会出现这样的结果呢,因为是Integer缓存了-128到127的数值,当使用"=="比较符时,实际上比较的是两个对象的地址,因为Integer将-128到127的数值都进行了缓存,所以在这个范围内的相同的两个值无论是使用==还是equals比较结果都是true,因为它们是直接取的缓存中的值,但是不在这个范围内的话,则返回的是false。

当使用的是equals方法比较时,不在这个范围内的相同的两个值返回的结果也是true,因为Integer内部重写了equals方法,该方法比较的是两个对象的中的值而不是地址。

建议:使用包装类比较值时,不要使用"==",而应该使用equals方法。

标签:删除,list,equals,remove,add,日常,思考,淘宝,中常
来源: https://blog.csdn.net/mxy2404830/article/details/122599588

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有