遍历Map删除指定元素

假如有一个Map和一个Set,我们要删除Map中key在Set里面的元素,这种情况只需遍历Set,然后分别删除Map中对应的元素即可。但假如我们要删除Map中key不在Set里面的元素,这时就不能遍历Set了,此时需要遍历Map,如下:

		Map<String, String> map = new HashMap<>();
		map.put("a", "a");
		map.put("b", "b");
		map.put("c", "c");
		map.put("d", "d");
		Set<String> set = new HashSet<>();
		set.add("b");
		set.add("c");
		//遍历map,删除key不在set里面的元素
		for(String key:map.keySet()) {
			if(!set.contains(key))
				map.remove(key);
		}

但很可惜,这个代码是错的,我们不能在遍历Map/Set的同时删除被遍历对象的元素,否则会报错:

ConcurrentModificationException

我们可以通过Iterator来实现:

		Map<String, String> map = new HashMap<>();
		map.put("a", "a");
		map.put("b", "b");
		map.put("c", "c");
		map.put("d", "d");
		Set<String> set = new HashSet<>();
		set.add("b");
		set.add("c");
		//遍历map,删除key不在set里面的元素
		Iterator<String>iterator = map.keySet().iterator();
		while (iterator.hasNext()) {
			String key = iterator.next();
			if(!set.contains(key))
				iterator.remove();
		}

报ConcurrentModificationException错误的原因如下:

ArrayList为例来分析:

在ArrayList里,有一个变量:protected transient int modCount = 0;

上面这个变量表示这个集合被结构性修改的次数。什么是结构性修改?看代码就会发现,是指add()、remove()、clear()、addAll()等增、删集合元素的动作。每当执行一次这些动作,上面的modCount变量就会加一,如下面代码展示:

public boolean add(E e) {
        modCount++;
        add(e, elementData, size);
        return true;
}

那么这个变量有什么用呢?通过代码发现,在使用Iterator迭代的时候会用到这个变量,如下面代码展示

//ArrayList里的iterator()方法
public synchronized Iterator<E> iterator() {
        return new Itr();
}
    /**
     * 上面方法会创建这个Itr对象
     * An optimized version of AbstractList.Itr
     */
    private class Itr implements Iterator<E> {
        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() {
            // Racy but within spec, since modifications are checked
            // within or after synchronization in next/previous
            return cursor != elementCount;
        }

        public E next() {
            synchronized (Vector.this) {
                //校验集合是否被修改过
                checkForComodification();
                int i = cursor;
                if (i >= elementCount)
                    throw new NoSuchElementException();
                cursor = i + 1;
                return elementData(lastRet = i);
            }
        }

        public void remove() {
            if (lastRet == -1)
                throw new IllegalStateException();
            synchronized (Vector.this) {
                checkForComodification();
                Vector.this.remove(lastRet);
                expectedModCount = modCount;
            }
            cursor = lastRet;
            lastRet = -1;
        }

        @Override
        public void forEachRemaining(Consumer<? super E> action) {
            Objects.requireNonNull(action);
            synchronized (Vector.this) {
                final int size = elementCount;
                int i = cursor;
                if (i >= size) {
                    return;
                }
                final Object[] es = elementData;
                if (i >= es.length)
                    throw new ConcurrentModificationException();
                while (i < size && modCount == expectedModCount)
                    action.accept(elementAt(es, i++));
                // update once at end of iteration to reduce heap write traffic
                cursor = i;
                lastRet = i - 1;
                checkForComodification();
            }
        }
        //校验当前时间点此集合的修改次数是否跟本对象创建时记录的次数一致,
        //如果不一致,则抛出ConcurrentModificationException异常。
        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }

另外,由此我们也看到,产生ConcurrentModificationException的地方就在这里,就是在Itr的next()、remove()、forEachRemaining()里面,它们都会校验集合是否被修改,如果有修改,则抛出ConcurrentModificationException异常。校验的原理也很简单,就是对比当前的集合的修改次数与当时Itr对象创建时记录的次数,如果不一致,就是被修改过了。

因此,在实际开发过程中,如果使用Iterator来对ArrayList这种集合进行迭代的过程中,对该集合进行了增、删元素操作,然后等下一轮走到next()方法时,next方法就会检测到集合的修改,就会抛出这个ConcurrentModificationException异常。
 

 

 

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值