百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

带了一个 3 年的开发,不会循环删除 List 中的元素,心态崩了......

toyiye 2024-06-21 12:05 9 浏览 0 评论

最近和某个朋友聊天,说他手下的一个开发,工作 3 年多了,一个需求的技术点,需要循环删除 List 中的元素,整了半天,说程序报错,不会弄。。

他挺无语的,和我倾诉,我说工作 3 年多也不至于吧,不会的话,在网上找找也能搞定啊,他说确实是的,这个开发挺难带的,简直崩溃!!

循环删除 List 中的元素,这个问题是有需要的注意点的,如果是个新手,确实会遇到一点麻烦,但工作 3 年多,我觉得应该不至于啊,好吧,这篇文章就来梳理一下这其中的道道。

问题

比如有以下这个 List:

public List<String> initList = Arrays.asList("张三", "李四", "周一", "刘四", "李强", "李白");

怎么删除 List 中姓李的人?

方法

我来们分析下有可行的方法!

1、普通 for 循环删除(不可靠)

/**
 * 普通 for 循环删除
 * @author: 栈长
 * @from: Java技术栈
 */
@Test
public void remove1() {
    List<String> list = new ArrayList(initList);
    for (int i = 0; i < list.size(); i++) {
        String str = list.get(i);
        if (str.startsWith("李")) {
            list.remove(i);
        }
    }
    System.out.println(list);
}

输出结果:

[张三, 周一, 刘四, 李白]

WC,李白没删干净?

问题就出在 list.size(),因为 list.size() 和 i 都是动态变化的,i 的值一直在累加,list.size() 一直在减少,所以 list 就会早早结束了循环。

所以这种方式虽然不会报错,但存在隐患,并且不容易被察觉,不建议使用。

2、普通 for 循环提取变量删除(抛异常)

把上面的示例中的 size 提出变量:

/**
 * 普通 for 循环删除(size提出变量)
 * @author: 栈长
 * @from: Java技术栈
 */
@Test
public void remove2() {
    List<String> list = new ArrayList(initList);
    int size = list.size();
    for (int i = 0; i < size; i++) {
        String str = list.get(i);
        if (str.startsWith("李")) {
            list.remove(i);
        }
    }
    System.out.println(list);
}

输出结果:

好家伙,都直接干下标溢出异常了。。

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

3、普通 for 循环倒序删除(可靠)

/**
 * 普通 for 循环倒序删除
 * @author: 栈长
 * @from: Java技术栈
 */
@Test
public void remove3() {
    List<String> list = new ArrayList(initList);
    for (int i = list.size() - 1; i > 0; i--) {
        String str = list.get(i);
        if (str.startsWith("李")) {
            list.remove(i);
        }
    }
    System.out.println(list);
}

输出结果:

[张三, 周一, 刘四]

结果输出正常,这种删除方式就算把 list.size() 提出变量也是 OK 的,因为循环中只用到了一次。

4、增强 for 循环删除(抛异常)

/**
 * 增强 for 循环删除
 * @author: 栈长
 * @from: Java技术栈
 */
@Test
public void remove3() {
    List<String> list = new ArrayList(initList);
    for (String element : list) {
        if (element.startsWith("李")) {
            list.remove(element);
        }
    }
    System.out.println(list);
}

输出结果:

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

java.util.ConcurrentModificationException

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

其实,for(xx in xx) 就是增强的 for循环,即迭代器 Iterator 的加强实现,其内部是调用的 Iterator 的方法,为什么会报 ConcurrentModificationException 错误,我们来看下源码:

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

5、迭代器循环迭代器删除(可靠)

/**
 * 迭代器循环删除(iterator.remove)
 * @author: 栈长
 * @from: Java技术栈
 */
@Test
public void remove4() {
    List<String> list = new ArrayList(initList);
    for (Iterator<String> iterator = list.iterator(); iterator.hasNext(); ) {
        String str = iterator.next();
        if (str.contains("李")) {
            iterator.remove();
        }
    }
    System.out.println(list);
}

输出结果:

[张三, 周一, 刘四]

结果输出正常,这是因为迭代器中的 remove 方法将期待修改的数量(expectedModCount)值进行了同步:

所以,这种删除方法是安全的,推荐使用。

6、迭代器循环集合删除(抛异常)

/**
 * 迭代器循环删除(list.remove)
 * @author: 栈长
 * @from: Java技术栈
 */
@Test
public void remove5() {
    List<String> list = new ArrayList(initList);
    for (Iterator<String> ite = list.iterator(); ite.hasNext(); ) {
        String str = ite.next();
        if (str.contains("李")) {
            list.remove(str);
        }
    }
    System.out.println(list);
}

输出结果:

又是那个并发修改异常,这个示例虽然使用了 Iterator 循环,但删除的时候却使用了 list.remove 方法,同样是有问题的,注意了,千万别用错了。

7、集合 forEach 方法循环删除(抛异常)

/**
 * list.forEach 删除
 * @author: 栈长
 * @from: Java技术栈
 */
@Test
public void remove6() {
    List<String> list = new ArrayList(initList);
    list.forEach((e) -> {
        if (e.contains("李")) {
            list.remove(e);
        }
    });
    System.out.println(list);
}

输出结果:

forEach 源码:

forEach 方法的背后其实就是增强的 for 循环,底层即迭代器,所以使用 list.remove 同样抛出 ConcurrentModificationException 异常。

8、stream filter 过滤(可靠)

/**
 * stream filter 过滤
 * @author: 栈长
 * @from: Java技术栈
 */
@Test
public void remove7() {
    List<String> list = new ArrayList(initList);
    list = list.stream().filter(e -> !e.startsWith("李")).collect(Collectors.toList());
    System.out.println(list);
}

输出结果:

[张三, 周一, 刘四]

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

总结

本文总结了 8 种循环删除 List 元素的方法:

  • 普通 for 循环删除(不可靠)
  • 普通 for 循环提取变量删除(抛异常)
  • 普通 for 循环倒序删除(可靠)
  • 增强 for 循环删除(抛异常)
  • 迭代器循环迭代器删除(可靠)
  • 迭代器循环集合删除(抛异常)
  • 集合 forEach 方法循环删除(抛异常)
  • stream filter 过滤(可靠)

可靠、可行的方案也只有 3 种,正统的删除法也就 2 种,但也要注意别用错了方法,所以也理解那个小伙子说怎么搞都搞不定,估计也是走了不少弯路,或者没有找对方法吧。

原文链接:https://mp.weixin.qq.com/s/j3k70kB3KPGO5ZKpOxJ4Pw

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码