List集合注意事项

一、添加元素时,如果元素为数字类型时,会有一个自动装箱的操作,也就是new Integer(20)。

 list.add(20);//等价于list.add(new Integer(20));

二、删除元素时,如果元素为数字类型,想要通过元素来进行删除,需要做一个装箱操作,new Integer()。

list.remove(new Integer(20));

三、remove()方法陷阱

List list = new ArrayList();
        list.add(20);
        list.add(20);
        list.add(20);
        list.add(50);
        list.add(60);
        for (int i = 0; i < list.size(); i++) {
            if (list.get(i).equals(20))
                list.remove(i);
        }
        System.out.println(list);

输出结果:[20, 50, 60]。

 

这时我们会发先集合中的20并没有被完全删除,这是为什么呢?

因为在List中,当我们去移除索引i位置的元素时,i位置右边的元素就会向左移动,也就是索引依次-1。所以说:

第一次循环时集合为:[20,20,20,50,60],删除的索引值为0,也就是[20,20,20,50,60]。

第二次循环时集合为:[20,20,50,60],删除的索引为1,也就是[20,20,50,60]。

第三次循环时集合为[20,50,60],删除的索引为2,而索引2位置元素不为20,所以不做删除。

这个时候我们会发现,list长度变为了3,而下次循环时i=3,不满足条件i<list.size(),所以这时循环结束。

 

解决方案:

1、删除元素时,将同步调整for循环索引

for (int i = 0; i < list.size(); i++) {
            System.out.println(list.size());
            if (list.get(i).equals(20)) {
                list.remove(i);
                i--;
            }
        }

输出结果:[50,60]

 

2、倒序删除

for (int i = list.size() - 1; i >= 0; i--) {
            if (list.get(i).equals(20)) {
                list.remove(i);
            }
        }

输出结果:[50,60]

 

3、使用迭代器iterator

Iterator it = list.iterator();
        while (it.hasNext()) {
            if (it.next().equals(20)) {
                it.remove();
            }
        }

输出结果:[50,60]

 

四、使用增强for删除list元素

for (Object o : list) {
            list.remove(o);
        }

抛出异常:java.util.ConcurrentModificationException。

增强for(foreach)实际上是对Iterable,hasNext(),next()方法的简写。通过List.iterator()源码可知,它返回了一个Itr对象。

public Iterator<E> iterator() {
        return new 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;

        Itr() {}

        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 < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

        @Override
        @SuppressWarnings("unchecked")
        public void forEachRemaining(Consumer<? super E> 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();
        }
    }

通过代码可知,Itr是ArrayList内部的一个私有类,实现了Iterator接口的功能。在next()方法和remove()方法中都调用了方法checkForComodification(),方法内部判断modCount != expectedModCount,则抛出new ConcurrentModificationException()异常。

 

 再看ArrayList的remove()方法:

 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;
    }
private void fastRemove(int index) {
        modCount++;
        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
    }

我们会发现在调用list的remove()方法时,针对modCount做了修改,而没有对list的迭代器内部的expectedModCount变量做操作,这时判断条件成立,就抛出了new ConcurrentModificationException()异常。

 

再看ArrayList内部迭代器内的remove()方法:

 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();
            }
        }

在调用ArrayList.remove()方法后,对expectedModCount 做了同步操作,所以不会出现此问题。

posted @ 2022-12-15 02:39  Amireux-126  阅读(32)  评论(0)    收藏  举报