在工作中的许多场景下,我们都会使用到List这个数据结构,那么同样的有很多场景下需要删除List中的某一个元素或某几个元素,那么我们该如何正确无误地删除List中的元素的,今天我来教大家三种方式。

前提知识准备

for循环的执行顺序

这里借用百度百科的一张图,简明扼要的介绍一下。

Iterator迭代器介绍

迭代器:迭代其实我们可以简单地理解为遍历,是一个标准化遍历各类容器里面的所有对象的方法类,它是一个很典型的设计模式。Iterator 模式是用于遍历集合类的标准访问方法。它可以把访问逻辑从不同类型的集合类中抽象出来,从而避免向每次遍历前都需要知道要遍历集合的内部结构。

// 遍历list
List list = new ArrayList();
list.add(1);
list.add(2);
for (int i = 0; i < list.size(); i++) {
Object object = list.get(i);
// do something
} // 遍历map
Map<String,String> map = new HashMap<>();
map.put("1","first");
map.put("2","second");
for (Map.Entry<String,String> entry : map.entrySet()){
String key = entry.getKey();
String value = entry.getValue();
// do something
}

对于这两种方式,我们总是都事先知道集合的内部结构,访问代码和集合本身是紧密耦合的,无法将访问逻辑从集合类和遍历方法中分离出来。同时每一种集合对应一种遍历方法,代码无法复用。
为了解决以上问题, Iterator 模式腾空出世,它总是用同一种逻辑来遍历集合。使得需要遍历集合的人,在遍历的时候不需要了解集合的内部结构,所有的内部状态都由 Iterator 来维护。遍历集合的方法不直接和集合类打交道,它总是控制 Iterator,向它发送”向前”,”向后”,”取当前元素”的命令,就可以间接遍历整个集合。

错误:for循环顺序遍历

直接使用简单for循环,以for (int i = 0; i < list.size(); i++) 进行遍历,这种方式可能会在遍历的过程中漏掉部分元素,从而出现少删的情况。

/**
* 通过简单的遍历方式,在遍历的过程中有可能会漏掉元素
* 取第二个元素i=1时,满足条件被删掉,原有的数组的第三个元素,变成了新数组的第二个元素
* i++后i=2,但i=2指向的是新数组中的第三个元素,那么原数组中的第三个元素就被漏掉了
*
* @param list
* @param element
* @return
*/
public static List forRemove(List list, Object element) {
for (int i = 0; i < list.size(); i++) {
if (element.equals(list.get(i))) {
list.remove(i);
}
}
return list;
}

错误:增强for循环,删除后不退出

使用增强for循环是,如果删除后继续向下循环则会报java.util.ConcurrentModificationException

/**
* 使用增强for循环是,如果删除后继续向下循环则会报
* java.util.ConcurrentModificationException
*
* @param list
* @param element
* @return
*/
public static List forceForRemove(List list, Object element) {
for (Object item : list) {
if (item.equals(element)) {
list.remove(item);
}
}
return list;
}

异常如下:

Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
at com.lingyejun.leetcode.RemoveListElement.forceForRemove(RemoveListElement.java:57)
at com.lingyejun.leetcode.RemoveListElement.main(RemoveListElement.java:112)

正确:逆向循环遍历

我们使用逆向遍历的方式可以得到正确的结果

/**
* 逆向循环,是正确的
* 1-->2-->3-->4
* 逆向循环时,倒数第一个元素满足条件被删除时,i--后,原数组的倒数第二个变成了新数组的倒数第一个元素
* i = size-2指向新数组的最后一个元素,没有漏掉。
* 同理倒数第二个元素满足条件被删除时,i--后,原数组的倒数第三个变成了新数组的倒数第二个元素
* i= size-3指向新数组的倒数第二个元素,也没有漏掉
*
* @param list
* @param element
* @return
*/
public static List reverseorRemove(List list, Object element) {
for (int i = list.size() - 1; i >= 0; i--) {
if (element.equals(list.get(i))) {
list.remove(i);
}
}
return list;
}

正确但不优雅:增强for循环删除元素后break

使用增强for循环,删除元素后,立即跳出,则正常退出,但缺点是不能向后继续循环了。  

/**
* 删除元素后,立即跳出,则正常退出,但不能向后继续循环了
*
* @param list
* @param element
* @return
*/
public static List forceForRemove1(List list, Object element) {
for (Object item : list) {
if (item.equals(element)) {
// 删除后立马终端循环,会正常跳出,但代价是不能继续向后循环了
list.remove(item);
break;
}
}
return list;
}

优雅删除:使用Iterator迭代器

使用迭代器可,正确无误的删除,代码简洁优雅,推荐使用!

/**
* 使用迭代器可,正确无误的删除
*
* @param list
* @param element
* @return
*/
public static List iteratorRemove(List list, Object element) {
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
Object cur = iterator.next();
if (cur.equals(element)) {
// 注意!!!这里时Iterator.remove()!!!而不是list.remove()!!!
iterator.remove();
}
}
return list;
}

参考文章:

https://jingyan.baidu.com/article/7f766dafaa6ee04101e1d0e6.html

http://wiki.jikexueyuan.com/project/java-enhancement/java-thirty.html

Java中如何优雅地删除List中的元素的更多相关文章

  1. Java 添加、替换、删除PDF中的图片

    概述 本文介绍通过java程序向PDF文档添加图片,以及替换和删除PDF中已有的图片.另外,关于图片的操作还可参考设置PDF 图片背景.设置PDF图片水印.读取PDF中的图片.将PDF保存为图片等文章 ...

  2. Java 添加、读取、删除Excel中的图表趋势线

    本文以Java示例介绍如何在Excel中添加趋势线,以及读取趋势线公式.通过文中的方法可支持添加6种不同类型的趋势线,包括Linear.Exponential.Logarithmic.Moving A ...

  3. MongoDB中如何优雅地删除大量数据

    删除大量数据,无论是在哪种数据库中,都是一个普遍性的需求.除了正常的业务需求,我们需要通过这种方式来为数据库"瘦身". 为什么要"瘦身"呢? 表的数据量到达一定 ...

  4. oracle中的rowid--伪列-删除表中的重复内容-实用

    1.rowid是一个伪列,是用来确保表中行的唯一性,它并不能指示出行的物理位置,但可以用来定位行. 2.rowid是存储在索引中的一组既定的值(当行确定后).我们可以像表中普通的列一样将它选出来. 3 ...

  5. 如何优雅地删除 Linux 中的垃圾文件

    不知道大家是否也跟我一样,是一只要把的自己电脑文件安排的条理有序,把没用的文件会及时删掉的程序猿呢?如果是的话,那么我们可以愉快地探讨下文章的内容.如果不是的话,你也可以留下来凑凑热闹嘛(>-& ...

  6. Linux中通过命令直接删除文件中最后一行

    何谓Sed(Stream EDitor):Sed原为UNIX系统上的非交谈式文字编辑器(non-interactive stream editor).当Sed读入待编辑文件,会依编辑命令来进行文件的编 ...

  7. 如何删除datatable中的一行数据

    在C#中,如果要删除DataTable中的某一行,大约有以下几种办法: 1,使用DataTable.Rows.Remove(DataRow),或者DataTable.Rows.RemoveAt(ind ...

  8. Java 8 中如何优雅的处理集合

    Java 8 中如何优雅的处理集合(Stream API) 在Java中,集合和数组是我们经常会用到的数据结构,需要经常对他们做增.删.改.查.聚合.统计.过滤等操作.相比之下,关系型数据库中也同样有 ...

  9. [java]删除数组中的某一个元素

    package org.company.project.test; import java.util.Arrays; import java.util.Scanner; public class Ar ...

随机推荐

  1. JavaScript 异步进化史

    前言 JS 中最基础的异步调用方式是 callback,它将回调函数 callback 传给异步 API,由浏览器或 Node 在异步完成后,通知 JS 引擎调用 callback.对于简单的异步操作 ...

  2. hihoCoder挑战赛28 题目1 : 异或排序

    题目1 : 异或排序 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 给定一个长度为 n 的非负整数序列 a[1..n] 你需要求有多少个非负整数 S 满足以下两个条件: ...

  3. PostgreSQL9.3安装tds_fdw扩展

    PostgreSQL从9.x开始支持所谓的外表的功能,就是在PostgreSQL中通过安装一些扩展再进行一些配置可以在本地建立一个外表映射到其他不同类型的数据库(可以大致理解为跨越PostgreSQL ...

  4. 23种设计模式之适配器模式(Adapter)

    适配器模式将一个接口转换成客户希望的另一个接口,从而使接口不兼容的那些类可以一起工作.适配器模式既可以作为类结构型模式,也可以作为对象结构型模式.在类适配器模式中,通过使用一个具体类将适配者适配到目标 ...

  5. “找女神要QQ号码”——java篇

    题目就是这样的: 给了一串数字(不是QQ号码),根据下面规则可以找出QQ号码: 首先删除第一个数,紧接着将第二个数放到这串数字的末尾,再将第三个数删除,并将第四个数放到这串数字的末尾...... 如此 ...

  6. ThreadLocal Java并发

    ThreadLocal 文章来源:http://con.zhangjikai.com/ThreadLocal.html ThreadLocal 主要用来提供线程局部变量,也就是变量只对当前线程可见. ...

  7. React 组件协同关系

    组件协同的两种方法,1种是纵向的协同,就是组件嵌套,重点在于代码的封装,2种是横向协同也就是Mixin,组件抽离,重点在于代码复用 1.组件嵌套,父组件通过属性向子组件,子组件可以通过事件处理函数以委 ...

  8. Nginx限制IP访问及获取客户端realip实战

    做网站时经常会用到remote_addr和x_forwarded_for这两个头信息来获取客户端的IP,然而当有反向代理或者CDN的情况下,这两个值就不够准确了,需要调整一些配置.Nginx作为web ...

  9. 11.23CSS笔记

    每一步的编写过程都在代码里面书写了,暂且记录下来 <!DOCTYPE html> <html lang="en"> <head> <met ...

  10. iOS多线程编程之GCD介绍(转载)

    一.简单介绍 1.什么是GCD? 全称是Grand Central Dispatch,可译为“牛逼的中枢调度器” 纯C语言,提供了非常多强大的函数 2.GCD的优势 GCD是苹果公司为多核的并行运算提 ...