Java 集合中遍历删除元素抛异常的原因,从ArrayList说起 您所在的位置:网站首页 红外线治疗仪来例假可以用吗有影响吗 Java 集合中遍历删除元素抛异常的原因,从ArrayList说起

Java 集合中遍历删除元素抛异常的原因,从ArrayList说起

2024-05-31 00:01| 来源: 网络整理| 查看: 265

文章目录 使用for删除遗漏元素的原因:使用foreach删除报错的原因:其它集合的删除方法删除集合元素的工具类 ArrayList删除元素的方式。

使用for循环删除。会遗漏删除的元素使用foreach删除。删除多个会抛ConcurrentModificationException异常。

测试用例

public class RemoveCollection { public static void main(String[] args) { List list = new ArrayList(); list.add("hel"); list.add("peo"); list.add("she"); list.add("he"); list.add("he"); list.add("haha"); forRemove(list,"he"); //结果[hel, peo, she, he, haha] // forEachRemove(list,"he");//抛出异常 // System.out.println(RemoveCollectionUtils.removeList(list, "he", "hel")); System.out.println(list); } public static void forRemove(List list, T obj) { for (int i = 0; i list.remove(obj); } } } public static void forEachRemove(List list, T obj) { for (T element:list) { if(element.equals(obj)){ list.remove(obj); } } } } 使用for删除遗漏元素的原因:

当删除的时候size值改变,list中删除元素的后面元素会向前移。导致下次遍历的时候错过元素。从后面开始遍历,则不会遗漏元素。比如把forRemove改成如下代码。

public static void forRemove(List list, T obj) { for (int i = list.size()-1; i >= 0; i--) { if(list.get(i).equals(obj)){ list.remove(obj); } } }

总结: 使用for循环删除时,逆序比较删除即可。

使用foreach删除报错的原因:

实现Iterable接口的类,均可使用foreach这种语法。实质上会调用实现类的iterator方法,接着调用hasNext()、next()方法。ArrayList中的继承关系图: 在这里插入图片描述

ArrayList中iterator方法。

public Iterator iterator() { return new Itr(); }

AbstractList中有一个modCount变量。在new Itr 时modCount值赋给expectedModCount。调用next方法时会校验modCount与expectedModCount值,不相同抛出异常。Itr()类的代码

private class Itr implements Iterator { int cursor; // index of next element to return int lastRet = -1; // index of last element returned; -1 if no such int expectedModCount = modCount; public boolean hasNext() { return cursor != size; } @SuppressWarnings("unchecked") 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]; } public void remove() { if (lastRet throw new ConcurrentModificationException(); } } @Override @SuppressWarnings("unchecked") public void forEachRemaining(Consumer consumer) { Objects.requireNonNull(consumer); final int size = ArrayList.this.size; int i = cursor; if (i >= size) { return; } final Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) { throw new ConcurrentModificationException(); } while (i != size && modCount == expectedModCount) { consumer.accept((E) elementData[i++]); } // update once at end of iteration to reduce heap write traffic cursor = i; lastRet = i - 1; checkForComodification(); } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }

ArrayList中的remove方法。每次remove的时候modCount就会加一。add方法的也会modCount就会加一。导致remove之后,下次遍历的时候,即调用next()方法的时候,抛出异常。

public E remove(int index) { rangeCheck(index); modCount++; E oldValue = elementData(index); int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // clear to let GC do its work return oldValue; }

解决办法: ①若确定list中只有一个元素,那么找到此元素之后进行break; ②有多个则 Iterator iterator = list.iterator();可能会有疑问,foreach的时候调用的就是iterator方法啊,为什么这种可以?使用foreach的时候调用的remove方法是ArrayList中的。使用list.iterator()进行删除时调用的是Itr类中的remove方法。

其它集合的删除方法

线程不安全的类都有modCount变量。如以下类:

ArrayListLinkedListHashMapLinkedHashMapTreeMapHashSetLinkedHashSetTreeSet 以上类中在删除元素时注意,因Map的key与Set的元素不能重复顾可使用foreach break的方式。其余参考Arraylist删除时的解决办法。 总结: 以上类在执行add跟remove方法时都会改变modCount的值。 删除集合元素的工具类

可以根据元素内容进行删除。支持可变参数。比较时调用的是集合元素中的equals方法。

public abstract class RemoveCollectionUtils { public static List removeList(List list, T obj) { //以空间换时间,不用扩容 List removeList = new ArrayList(list.size()); Iterator iterator = list.iterator(); while (iterator.hasNext()) { //一次hasNext只能一次 T t = iterator.next(); if (t.equals(obj)) { removeList.add(t); iterator.remove(); } } return removeList; } public static List removeList(List list, T... objArray) { //以空间换时间,不用扩容 List removeList = new ArrayList(list.size()); for (T obj : objArray) { removeList.addAll(removeList(list, obj)); } return removeList; } public static K removeMapByKey(Map map, K key) { //因为key在Map中只有一个 Iterator itMap = map.entrySet().iterator(); while (itMap.hasNext()) { K removeKey = itMap.next().getKey(); if (removeKey.equals(key)) { itMap.remove(); return removeKey; } } return null; } public static List removeMapByValue(Map map, V value) { //因为key在Map中只有一个 List removeListValue = new ArrayList(map.size()); Iterator itMap = map.entrySet().iterator(); while (itMap.hasNext()) { V removeValue = itMap.next().getValue(); if (removeValue.equals(value)) { itMap.remove(); removeListValue.add(removeValue); return removeListValue; } } return removeListValue; } public static List removeMapByValue(Map map, V... valueArray) { //因为key在Map中只有一个 List removeListValue = new ArrayList(map.size()); for (V obj : valueArray) { removeListValue.addAll(removeMapByValue(map, obj)); } return removeListValue; } public static K removeSet(Set set, K key) { //因为key在Set中只有一个 Iterator iteratorKey = set.iterator(); while (iteratorKey.hasNext()) { K removeKey = iteratorKey.next(); if (removeKey.equals(key)) { iteratorKey.remove(); return removeKey; } } //没有找到返回null return null; } }

每篇一语:

“你的醒来,使我欢喜。我正在想着走出冰谷的方法;我愿意携带你去,使你永不冰结,永得燃烧。” “唉唉!那么,我将烧完!” “你的烧完,使我惋惜。我便将你留下,仍在这里罢。” “唉唉!那么,我将冻灭了!” ——《死火》 鲁迅



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有