美文网首页
如何操作你的ArrayList

如何操作你的ArrayList

作者: fanyank | 来源:发表于2019-11-22 02:42 被阅读0次

    错误的写法

    1. 不会抛异常,但是会漏掉数据
    List<Integer> list = new ArrayList<>();
    list.add()...
    for(int i = 0;i < list.size();i++) {
                if(i == xxx) {
                    list.remove(i);
                }
            }
    

    该种写法每一次随着条件的成立,会带来如下问题

    • 每一次remove掉一个元素,导致数组后面所有的所有元素向前移位,之后索引本身自增,结果就是reomve后面的那个元素会被漏掉检查
    1. 快速抛异常
    List<Integer> list = new ArrayList<>();
    list.add()...
    while (iterator.hasNext()) {
        list.remove(iterator.next());
    }
    

    该种方法会快速的抛出异常,相同的写法如下:

    List<Integer> list = new ArrayList<>();
    list.add()...
    for(Integer integer : list) {
        list.remove(integer);
    }
    

    之所以写法相同是因为forEach循环经过JVM解析之后的指令同Iterator一样,可见第二种写法仅仅是Java提供的一种语法糖。快速抛出异常的原因参见ArrayList源码,部分源码如下:

        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
        }
    

    每次remove()操作都会修改modCount的值,但是每一调用iterator.next()方法会检查exceptedModCount==modCount, 如果不相等就会抛出ConcurrentModificationException, 部分源码如下:

        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];
         }
    
        final void checkForComodification() {
                if (modCount != expectedModCount)
                    throw new ConcurrentModificationException();
        }
    

    正确的写法

    1. 巧妙的写法
            for(int i = list.size()-1;i >= 0;i--) {
                list.remove(i);
            }
    

    从后向前遍历,每次remove操作之后,后面的元素仍然向前移动,但是前面元素的顺序不便,因此索引自增之后指向的仍然原来应该指向的元素。

    1. 官方推荐的写法
           while (iterator.hasNext()) {
               iterator.next();
               iterator.remove();
           }
    

    iterator.remove()方法不同于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();
                }
            }
    

    每次remove()操作都会将modCount同步到exceptedModCount值中,这样就可以安全的删除数组中的元素。

    官方的话

     * The iterators returned by this class's {@link #iterator() iterator} and
     * {@link #listIterator(int) listIterator} methods are <em>fail-fast</em>:</a>
     * if the list is structurally modified at any time after the iterator is
     * created, in any way except through the iterator's own
     * {@link ListIterator#remove() remove} or
     * {@link ListIterator#add(Object) add} methods, the iterator will throw a
     * {@link ConcurrentModificationException}.  Thus, in the face of
     * concurrent modification, the iterator fails quickly and cleanly, rather
     * than risking arbitrary, non-deterministic behavior at an undetermined
     * time in the future.
    

    相关文章

      网友评论

          本文标题:如何操作你的ArrayList

          本文链接:https://www.haomeiwen.com/subject/kvtfwctx.html