java中List移除元素的四种方式
作者:遨游在知识的海洋里无法自拔
四种方式:
- 方式一,使用 Iterator ,顺序向下,如果找到元素,则使用 remove 方法进行移除。
- 方式二,倒序遍历 List ,如果找到元素,则使用 remove 方法进行移除。
- 方式三,正序遍历 List ,如果找到元素,则使用 remove 方法进行移除,然后进行索引 “自减”。
- 方式四,使用jdk1.8新增的Stream流操作
1.Iterator 迭代器
@Test public void fun9(){ List<String> list = new ArrayList<>(); list.add("赵云"); list.add("黄忠"); list.add("马超"); list.add("关羽"); list.add("张飞"); // 获取迭代器 Iterator<String> it = list.iterator(); while(it.hasNext()){ String str = it.next(); if("关羽".equals(str)){ it.remove(); } } System.out.println(list); }
2.倒序遍历
@Test public void fun10(){ List<String> list = new ArrayList<>(); list.add("赵云"); list.add("黄忠"); list.add("马超"); list.add("关羽"); list.add("张飞"); for (int i = list.size() - 1; i > 0; i--) { if("关羽".equals(list.get(i))){ list.remove(i); } } System.out.println(list); }
3.正序遍历
@Test public void fun11(){ List<String> list = new ArrayList<>(); list.add("赵云"); list.add("黄忠"); list.add("马超"); list.add("关羽"); list.add("张飞"); for (int i = 0; i < list.size(); i++) { if("关羽".equals(list.get(i))){ list.remove(i); i--; } } System.out.println(list); }
4.Stream流操作(JDK 1.8 +)
@Test public void fun8(){ List<String> list = new ArrayList<>(); list.add("赵云"); list.add("黄忠"); list.add("马超"); list.add("关羽"); list.add("张飞"); // 筛选出不是“关羽” 的集合 list = list.stream().filter(e -> !"关羽".equals(e)).collect(Collectors.toList()); System.out.println("method4|list=" + list); }
问题:
1.为什么不能使用forEach
@Test public void fun5(){ List<String> list = new ArrayList<>(); list.add("赵云"); list.add("黄忠"); list.add("马超"); list.add("关羽"); list.add("张飞"); for (String str :list) { if ("张飞".equals(str)){ list.remove(str); } } System.out.println(list); }
原因:
foreach方式遍历元素的时候,是生成iterator,然后使用iterator遍历。在生成iterator的时候,会保存一个expectedModCount参数,这个是生成iterator的时候List中修改元素的次数。如果你在遍历过程中删除元素,List中modCount就会变化,如果这个modCount和exceptedModCount不一致,就会抛出异常。这个是为了安全的考虑。如果使用iterator遍历过程中,使用List修改了元素,可能会出现不正常的现象。如果使用iterator的remove方法则会正常,因为iterator的remove方法会在内部调用List的remove方法,但是会修改excepedModCount的值,因此会正常运行。
2.为什么forEach 删除倒数第二元素不会出现异常
@Test public void fun12() { List<String> list = new ArrayList<>(); list.add("赵云"); list.add("黄忠"); list.add("马超"); list.add("关羽"); list.add("张飞"); for (String str:list) { if("关羽".equals(str)){ list.remove(str); } System.out.println(str); } }
仔细观察发现集合最后一个元素(“张飞”)并没有被遍历出来,因为当我们移除倒数第二个元素(“关羽”)时 cursor(游标)为 4 ,list 中size 属性的值会发生变化(5 - 1 = 4)变为 4,所以下面代码返回 false ,也就不会继续向下遍历。这也是能够正常执行的原因,因为如果继续遍历就会出现问题1 中的情况,在进行checkForComodification() 时,因为 modCount 发生了变化,而expectedModCount 并没有发生变化,所以会出现 ConcurrentModificationException异常。
public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { ArrayList.this.remove(lastRet); cursor = lastRet; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } }
public boolean hasNext() { return cursor != size; }
public E next() { checkForComodification(); int i = cursor; if (i >= size) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1; return (E) elementData[lastRet = i]; }
final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }
3 普通正序 for 循环为什么要 i –
因为遍历过程中进行remove 操作时,该位置后面的元素会挤到前面来,这时候会发生一种情况就是原来元素的位置会被他后面的元素取代,而该位置已经遍历过了,所以该元素不会背遍历。 所以要进行 i-- 操作从该位置重新遍历。
@Test public void fun11(){ List<String> list = new ArrayList<>(); list.add("赵云"); list.add("黄忠"); list.add("马超"); list.add("关羽"); list.add("张飞"); for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i)); if("关羽".equals(list.get(i))){ list.remove(i); } } System.out.println(list); }
就是下面的情况 “张飞” 不见了…
4 为什么倒序for 循环可以
当我们倒序遍历元素的时候,无论删除元素之后的元素怎么移动,之前的元素对应的索引(index)是不会发生变化的,所以在删除元素的时候不会发生问题。
到此这篇关于java中List移除元素的四种方式的文章就介绍到这了,更多相关java List移除元素内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!