遍历集合删除元素

一、根据下标删除元素

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 来进行集合删除的操作。

猜你喜欢

转载自mingyundezuoan.iteye.com/blog/2397484
今日推荐