网站首页 > 基础教程 正文
最近和某个朋友聊天,说他手下的一个开发,工作 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
猜你喜欢
- 2024-12-24 HashMap如何添加、删除元素? hashmap添加元素的方法
- 2024-12-24 C++ 使用统一擦除函数从容器中删除项目
- 2024-12-24 Python 30 天提升:数据结构之列表与元组全解析
- 2024-12-24 C#知识|泛型集合List相关方法 c# 泛型列表
- 2024-12-24 Python精讲:在Python中添加、修改、删除和更新字典元素详解
- 2024-12-24 Java集合-List Java集合框架
- 2024-12-24 Java 如何从一个 List 中随机获得元素
- 2024-12-24 自学Python第九天——操作列表 python的基础操作
- 2024-12-24 面试 - 为什么foreach中不允许对元素进行add和remove
- 2024-12-24 从 Excel 列表中删除或提取包含关键字的区域,均一步到位
- 最近发表
- 标签列表
-
- gitpush (61)
- pythonif (68)
- location.href (57)
- tail-f (57)
- pythonifelse (59)
- deletesql (62)
- c++模板 (62)
- css3动画 (57)
- c#event (59)
- linuxgzip (68)
- 字符串连接 (73)
- nginx配置文件详解 (61)
- html标签 (69)
- c++初始化列表 (64)
- exec命令 (59)
- canvasfilltext (58)
- mysqlinnodbmyisam区别 (63)
- arraylistadd (66)
- node教程 (59)
- console.table (62)
- c++time_t (58)
- phpcookie (58)
- mysqldatesub函数 (63)
- window10java环境变量设置 (66)
- c++虚函数和纯虚函数的区别 (66)