欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

遍历集合删除元素

程序员文章站 2022-05-02 09:49:37
...
一、根据下标删除元素

1.测试代码
		ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c", "d"));
		for(int index = 0 ; index < list.size() ; index++){
			if(Objects.equals("a", list.get(index))){
				list.remove(index);
			}
		}
		System.out.println(String.valueOf(list));


2.实现原理

    /**
     * Removes the element at the specified position in this list.
     * Shifts any subsequent elements to the left (subtracts one from their
     * indices).
     * 
     * @param index the index of the element to be removed
     * @return the element that was removed from the list
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    // 在列表中指定位置删除元素。
    // 所有后续元素左移(下标减1)。
    public E remove(int index) {
        rangeCheck(index); // 是否越界,index 是否大于了 数组的长度

        modCount++; // 操作次数
        E oldValue = elementData(index);

        int numMoved = size - index - 1;
        // 将index+1后numMoved个元素拷贝到index的位置上
        // 覆盖index位置上的元素,多出的一个位置设置为null,等待GC
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }



3.其他情况
删除单个元素,可以使用此方式,但多个元素的情况下不可以
因为删除一个元素后,数组的大小以及原来元素的下标都会改变;需要做相应的处理才可。

		ArrayList<String> list1 = new ArrayList<String>(Arrays.asList("a", "b", "c", "d"));
		for(int index = 0 ; index < list1.size() ; index++){
			// 删除 index = 0 即 a 元素后,数组大小变为3,b的下标变为0,所以再次删除的是c,最后退出循环
			list1.remove(index);
		}
		System.out.println(String.valueOf(list1));



二、foreach

1.代码实现
		for (String s : list) {
		    if (s.equals("a"))
		        list.remove(s);
		} 


运行抛出异常

Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
at java.util.ArrayList$Itr.next(ArrayList.java:831)


2.异常分析

        // 异常抛出的位置
        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }

    /**
     * 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() {
            return cursor != size;
        }

        @SuppressWarnings("unchecked")
        public E next() {
            checkForComodification();// 对应的831行代码
            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];
        }
}


从定位的异常信息看遍历的过程中进行了 chcek

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }


modCount 实际对数组进行操作的次数,expectedModCount 预期操作次数
初始化
        int expectedModCount = modCount;

但在遍历的同时,执行了remove操作,导致了 modCount+1 ,与 expectedModCount 不一致

3.为什么删除元素的方法会调用 Next

java foreach 实现原理
中 foreach 实质就是Iterator 的调用实现

    /**
     * Returns an iterator over the elements in this list in proper sequence.
     *
     * <p>The returned iterator is <a href="#fail-fast"><i>fail-fast</i></a>.
     *
     * @return an iterator over the elements in this list in proper sequence
     */
    public Iterator<E> iterator() {
        return new Itr();
    }

    /**
     * An optimized version of AbstractList.Itr
     */
    private class Itr implements Iterator<E> {}


删除元素的实现过程
    public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }



即可以认为调用了 ArrayList 的 Iterator 方法,返回了 其内部实现的静态类 Itr

在foreach 的过程中,即 Iterator 的 next 操作的过程中 每次获取下一个元素前都会进行一次校验

抛出不允许同时进行修改操作的异常

三、Iterator

		Iterator<String> iterator = list.iterator();
		while(iterator.hasNext()){
			if(StringUtils.isNotEmpty(iterator.next())){
				iterator.remove();
			}
		}


将集合转为Iterator 通过 Iterator 来进行集合删除的操作。
相关标签: list collection