List和Set集合中iterator的fail-fast特性之区别

来源:互联网 发布:域名com cn net的区别 编辑:程序博客网 时间:2024/05/16 16:20

刚才看到一个问题,关于List和Set集合中iterator的fail-fast特性

先上代码:

List集合:

public class Test {public static void main(String[] args){List<String> L = new LinkedList();L.add("aaa");L.add("bbb");L.add("ccc");System.out.println(L);String delete = "bbb";for(Iterator<String> iter=L.iterator();iter.hasNext();){String now = iter.next();System.out.println(now);if(now==delete)L.remove(delete);}System.out.println(L);}}

运行无异常,List在用iterator遍历过程中,可以用List的remove方法删除其最后两个元素任意一个(不能同时删除吐舌头

Set集合:

public class Test {public static void main(String[] args){Set<String> S = new LinkedHashSet();S.add("aaa");S.add("bbb");S.add("ccc");System.out.println(S);String delete = "bbb";for(Iterator<String> iter=S.iterator();iter.hasNext();){String now = iter.next();System.out.println(now);if(now==delete)S.remove(delete);}System.out.println(S);}}

当在用iterator遍历过程中,用Set的remove方法删除倒数第二个元素时运行出现异常:

Exception in thread "main" java.util.ConcurrentModificationException
at java.util.LinkedHashMap$LinkedHashIterator.nextNode(Unknown Source)
at java.util.LinkedHashMap$LinkedKeyIterator.next(Unknown Source)
at nowcoder.Test.main(Test.java:18)

用Set的remove方法删除倒数第1个元素

public class Test {public static void main(String[] args){Set<String> S = new LinkedHashSet();S.add("aaa");S.add("bbb");S.add("ccc");System.out.println(S);String delete = "ccc";for(Iterator<String> iter=S.iterator();iter.hasNext();){String now = iter.next();System.out.println(now);if(now==delete)S.remove(delete);}System.out.println(S);}}
运行正常:

[aaa, bbb, ccc]aaabbbccc[aaa, bbb]

终于到了解决问题的时候了。。。

1、首先找到List和Set各自的iterator实现源代码

List:LinkedList->AbstractSequentialList找到

/**     * Returns an iterator over the elements in this list (in proper     * sequence).<p>     *     * This implementation merely returns a list iterator over the list.     *     * @return an iterator over the elements in this list (in proper sequence)     */    public Iterator<E> iterator() {        return listIterator();    }</span>
接着找。。。到AbstractList找到(由于太长,已删除无关代码)

    /**     * {@inheritDoc}     *     * <p>This implementation returns {@code listIterator(0)}.     *     * @see #listIterator(int)     */    public ListIterator<E> listIterator() {        return listIterator(0);    }
    public ListIterator<E> listIterator(final int index) {        rangeCheckForAdd(index);        return new ListItr(index);    }    private class Itr implements Iterator<E> {        /**         * Index of element to be returned by subsequent call to next.         */        int cursor = 0;        int expectedModCount = modCount;        public boolean hasNext() {            return cursor != size();        }        public E next() {            checkForComodification();            try {                int i = cursor;                E next = get(i);                lastRet = i;                cursor = i + 1;                return next;            } catch (IndexOutOfBoundsException e) {                checkForComodification();                throw new NoSuchElementException();            }        }        final void checkForComodification() {//            if (modCount != expectedModCount)                throw new ConcurrentModificationException();        }    }    private class ListItr extends Itr implements ListIterator<E> {        ListItr(int index) {            cursor = index;        }    }
由上面代码可以看出当List用iterator遍历到倒数第二个元素时(已调用next方法),cursor=2,用remove方法删除会使得size()减1变为2,于是当iter调用hasNext方法时,cursor==size(),返回false,结束遍历,无异常。

当List用iterator遍历到倒数第一个元素时(已调用next方法),cursor=3,用remove方法删除会使得size()减1变为2,于是当iter调用hasNext方法时,cursor!=size(),本应返回true,返回的却是false???结束遍历,无异常。我也不知为什么。。。求解


Set:寻找路径:LinkHashSet->HashSet

    public Iterator<E> iterator() {        return map.keySet().iterator();    }

->HashMap$KeySet->HasMap$KeyIterator

final class KeyIterator extends HashIterator        implements Iterator<K> {        public final K next() { return nextNode().key; }    }

->HashMap$HashIterator

abstract class HashIterator {        Node<K,V> next;        // next entry to return        Node<K,V> current;     // current entry        int expectedModCount;  // for fast-fail        int index;             // current slot        HashIterator() {            expectedModCount = modCount;            Node<K,V>[] t = table;            current = next = null;            index = 0;            if (t != null && size > 0) { // advance to first entry                do {} while (index < t.length && (next = t[index++]) == null);//指向第一个不为空的元素            }        }        public final boolean hasNext() {            return next != null;        }        final Node<K,V> nextNode() {            Node<K,V>[] t;            Node<K,V> e = next;            if (modCount != expectedModCount)                throw new ConcurrentModificationException();            if (e == null)                throw new NoSuchElementException();            if ((next = (current = e).next) == null && (t = table) != null) {                do {} while (index < t.length && (next = t[index++]) == null);//<span style="font-family: 'DejaVu Sans', Arial, Helvetica, sans-serif;">指向下一个不为空的元素</span>            }            return e;        }        public final void remove() {            Node<K,V> p = current;            if (p == null)                throw new IllegalStateException();            if (modCount != expectedModCount)                throw new ConcurrentModificationException();            current = null;            K key = p.key;            removeNode(hash(key), key, null, false, false);            expectedModCount = modCount;        }    }

从上面代码中可以看出,当Set用iterator遍历到倒数第二个元素时(已调用next方法),next(Node类型)指向倒数一个元素(节点),不为null,用remove方法删除会使得modCount加一而expectedModCount不变于是

modCount = expectedModCount+1 =》 modCount != expectedModCount

当iter调用hasNext方法时,next!=null ,返回true,调用next方法,即调用nextNode方法,抛出异常。

如果是在遍历到倒数第一个元素时,next=null,所以无论你干什么都可以,反正下一次hasNext会返回false。

这样看来,只要不是在遍历倒数第一个元素时,修改Set,都会使得modCount != expectedModCount,从而抛出异常。










0 0
原创粉丝点击