zoukankan      html  css  js  c++  java
  • ArrayList源码阅读笔记(1.8)

    ArrayList类的注解阅读

    
    /**
     * Resizable-array implementation of the <tt>List</tt> interface.  Implements
     * all optional list operations, and permits all elements, including
     * <tt>null</tt>.  In addition to implementing the <tt>List</tt> interface,
     * this class provides methods to manipulate the size of the array that is
     * used internally to store the list.  (This class is roughly equivalent to
     * <tt>Vector</tt>, except that it is unsynchronized.)
    
     
        这是一个实现了List接口的可变长度的数组。实现了list接口中的所有方法,允许存放所有的元素,包括null。除了实现list接口,该类还提供了一些方法来操作数组的大小,此数组被用来存储list的数据。(这个类和vector大致相当,除了它是线程不安全的)
    
    
    • ArrayList底层实现是数组,可存放重复数据,包括null,线程不安全
     *
     * <p>The <tt>size</tt>, <tt>isEmpty</tt>, <tt>get</tt>, <tt>set</tt>,
     * <tt>iterator</tt>, and <tt>listIterator</tt> operations run in constant
     * time.  The <tt>add</tt> operation runs in <i>amortized constant time</i>,
     * that is, adding n elements requires O(n) time.  All of the other operations
     * run in linear time (roughly speaking).  The constant factor is low compared
     * to that for the <tt>LinkedList</tt> implementation.
     *
     
        size(),isEmpty(),get(),set()方法使用的一个恒定时间(一个方法具有恒定的执行时间的,也就是代码不会因为问题规模n的变化而发生变化,时间复杂度记为O(1))。add操作花费恒定分摊时间,即插入n个元素需要o(n)的时间。 粗略的来说所有其他操作都以线性时间运行。(即这些操作与元素的个数成线性关系,操作的时间复杂度o(n))。这些操作与LinkedList实现相比,常数因子较低。
     
    
    • 底层为数组结构,相对于LinkedList效率较高
     * <p>Each <tt>ArrayList</tt> instance has a <i>capacity</i>.  The capacity is
     * the size of the array used to store the elements in the list.  It is always
     * at least as large as the list size.  As elements are added to an ArrayList,
     * its capacity grows automatically.  The details of the growth policy are not
     * specified beyond the fact that adding an element has constant amortized
     * time cost.
     *
        每个ArrayList的实例对象都有一个容量(capacity)。这个容量就是这个list中用来存储元素的数组的大小。它至少和list的大小一样大。当有元素被增加到集合中时,它的容量会自动增加。除了要求添加一个元素的效率为“恒定分摊时间”,对于具体实现的细节没有特别的要求。
        
    
     * <p>An application can increase the capacity of an <tt>ArrayList</tt> instance
     * before adding a large number of elements using the <tt>ensureCapacity</tt>
     * operation.  This may reduce the amount of incremental reallocation.
     
        在大批量插入元素前,使用ensureCapacity()方法来增加集合的容量。这或许能够减少扩容增加量的大小。
    
     * <p><strong>Note that this implementation is not synchronized.</strong>
     * If multiple threads access an <tt>ArrayList</tt> instance concurrently,
     * and at least one of the threads modifies the list structurally, it
     * <i>must</i> be synchronized externally.  (A structural modification is
     * any operation that adds or deletes one or more elements, or explicitly
     * resizes the backing array; merely setting the value of an element is not
     * a structural modification.)  This is typically accomplished by
     * synchronizing on some object that naturally encapsulates the list.
     *
     
        注意这个实现类是非同步的。如果有多个线程同时操作一个ArrayList的实例。然后,至少有一个线程修改了list的结构,就必须在外部保证它的线程同步。(结构修改指的是增加或者删除一个或多个映射;如果仅仅是更改已经存在的
    key和value值,不算做结构修改)。这通常需要在一个被封装好的list对象上,使用同步进行操作。
     
    
    • ArrayList是非同步的,线程不安全的
     * If no such object exists, the list should be "wrapped" using the
     * {@link Collections#synchronizedList Collections.synchronizedList}
     * method.  This is best done at creation time, to prevent accidental
     * unsynchronized access to the list:<pre>
     *   List list = Collections.synchronizedList(new ArrayList(...));</pre>
     *
     
        如果没有这样的对象存在,那么就需要使用Collections.synchronizedList方法来包装这个list对象,而且最好是在创建对象的时候就进行包装,这是为了预防对这个list对象进行一些线程不同步的操作。举个例子:List list = Collections.synchronizedList(new ArrayList(...));
     
    
    • 尽管有一个线程安全的类Vector和ArrayList结构类似,但是我们在需要保证线程安全时依然不会使用Vector这个类(过时的类),而是使用 Collections.synchronizedList()方法来包装得到一个线程安全的list对象。
     * <p><a name="fail-fast">
     * 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.
     *
     
        该类的集合视图方法返回的迭代器是fail-fast机制的:在迭代器被创建后,如果list对象被结构化修改后,无论在何时,使用何种方法(除了迭代器本身的remove方法和add方法)来修改它,都会抛出ConcurrentModificationException.因此,面对并发修改操作时,迭代器会迅速且清晰地报错.而不是冒着在不确定的时间做不确定的操作的风险.
    
    
    • fail-fast,它是Java集合中的一种错误检测机制。某个线程在对collection进行迭代时,不允许其他线程对该collection进行结构上的修改。否则程序就会抛出ConcurrentModificationException 异常。快速终止操作
     
     * <p>Note that the fail-fast behavior of an iterator cannot be guaranteed
     * as it is, generally speaking, impossible to make any hard guarantees in the
     * presence of unsynchronized concurrent modification.  Fail-fast iterators
     * throw {@code ConcurrentModificationException} on a best-effort basis.
     * Therefore, it would be wrong to write a program that depended on this
     * exception for its correctness:  <i>the fail-fast behavior of iterators
     * should be used only to detect bugs.</i>
    
        注意,迭代器的fail-fast行为是不能保证的.一般来说,保证非同步的同步操作是不太可能的.在最优基础上,Fail-fast迭代器会抛出ConcurrentModificationException.因此,写一个为了自身正确性而依赖于这个异常的程序是不对的.迭代器的fail-fast行为应该只是用来检测bug而已.
    
    
    • 我们要主动封装list以便进行同步操作,程序要要避免此异常而不是使用此异常

    ArrayList类的定义

    public class ArrayList<E> extends AbstractList<E>
            implements List<E>, RandomAccess, Cloneable, java.io.Serializable
    
    • 继承的类
      AbstractList:抽象类,只需知道此类是List接口的简单通用实现

    • 实现的接口
      List:不多说了

      RandomAccess:(标记接口)代表支持随机访问
      Cloneable:(标记接口)代表 Object.clone() 方法可以合法地对该类实例进行按字段复制。(没有实现 Cloneable 接口的实例上调用 Object 的 clone 方法,则会导致抛出 CloneNotSupportedException 异常)
      java.io.Serializable(标记接口)

    属性的定义

        protected transient int modCount = 0;
        
    
    • 这是父类AbstractList的一个属性 :用于记录列表结构被修改的次数。每次列表结构被修改都会modCount++
      为什么要记录此数据呢?
      在线程不安全的集合中,正如上面所说:迭代器采用了fail-fast机制。而fail-fast机制触发原理就是比对expectedModCount 和 modCount 是否相等,不相等就报ConcurrentModificationException异常
      此处不理解没关系,后面会讲迭代器方法的源码时,就会明白了
    /**
         * Default initial capacity.
         * 初始默认容量 为 10
         */
        private static final int DEFAULT_CAPACITY = 10;
        
    
    /**
         * Shared empty array instance used for empty instances.
         * 
         * 指定该ArrayList容量为0时,返回该空数组。
         */
        private static final Object[] EMPTY_ELEMENTDATA = {};
    
    
    /**
         * Shared empty array instance used for default sized empty instances. We
         * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
         * first element is added.
         * 
         * 用于默认大小的空实例的共享空数组实例。
         * 这个空数组的实例用来给无参构造使用。当调用无参构造方法,返回的是该数组。
         * 将此与EMPTY_ELEMENTDATA区分开来,以便了解在添加第一个元素时要增加多少容量。
         */
        private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    
    
    • EMPTY_ELEMENTDATA 和 DEFAULTCAPACITY_EMPTY_ELEMENTDATA都是表示空数组实例
      区别在于:
      EMPTY_ELEMENTDATA 是用户指定初试容量为0时 使用它
      DEFAULTCAPACITY_EMPTY_ELEMENTDATA 是默认创建初始容量为10的空数组,被无参构造器使用
      将此与EMPTY_ELEMENTDATA区分开来,以便了解在添加第一个元素时要增加多少容量
    /**
         * The array buffer into which the elements of the ArrayList are stored.
         * The capacity of the ArrayList is the length of this array buffer. Any
         * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
         * will be expanded to DEFAULT_CAPACITY when the first element is added.
         * 
         * 存储ArrayList元素的数组缓冲区
         * ArrayList的容量(capacity)就是是此数组缓冲区的长度。
         * 声明为transient 不会被序列化
         * 非私有 是为了方便内部类调用
         */
        transient Object[] elementData;    // non-private to simplify nested class access
    
    
    • 该值为DEFAULTCAPACITY_EMPTY_ELEMENTDATA 时,当第一次添加元素进入ArrayList中时, 数组将扩容至DEFAULT_CAPACITY。这也印证了为什么区分EMPTY_ELEMENTDATA,DEFAULTCAPACITY_EMPTY_ELEMENTDATA
    /**
         * The size of the ArrayList (the number of elements it contains).
         *
         * List中元素的个数
         */
        private int size;
    
    
    • 仅仅表示 该list包含的元素的个数,和数组容量没有任何关系

    这里提一点:前面提到size()方法时间复杂度为O(1),是因为它将size存储起来了,牺牲了空间提高了效率。

    ArrayList构造器

    ArrayList有三个构造器

    创建一个指定存储容量的空序列
    
    public ArrayList(int initialCapacity) {
            if (initialCapacity > 0) {
                this.elementData = new Object[initialCapacity];
            } else if (initialCapacity == 0) {
                this.elementData = EMPTY_ELEMENTDATA;
            } else {
                throw new IllegalArgumentException("Illegal Capacity: "+
                                                   initialCapacity);
            }
        }
    
    • 指定的initialCapacity
      >0时,创建的序列容量为initialCapacity
      ==0时,创建的是 EMPTY_ELEMENTDATA
      <0时,会报异常
    无参构造,创建一个初始容量为10 的空序列
    
    public ArrayList() {
            this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    
    
    • 此处使用的是DEFAULTCAPACITY_EMPTY_ELEMENTDATA
    
    遵循集合的约定 提供 一个可将Collection转换为ArrayList的构造器
    这些元素是按照该 Collection 的迭代器返回它们的顺序排列的。
    
    public ArrayList(Collection<? extends E> c) {
            elementData = c.toArray();
            if ((size = elementData.length) != 0) {
                // c.toArray might (incorrectly) not return Object[] (see 6260652)
                if (elementData.getClass() != Object[].class)
                    elementData = Arrays.copyOf(elementData, size, Object[].class);
            } else {
                // replace with empty array.
                this.elementData = EMPTY_ELEMENTDATA;
            }
        }
    
    
    • 注意:toArray的方法返回的类型不一定是Object[] 这是jDK的bug,两个类之间有继承关系时,JDK允许向上转型(子类赋值给父类)而 向下转型(父类赋值给子类)是不允许的。为了解决这个问题需要检验运行时类型使用Arrays.copyOf将其转为Object[]类型

    核心方法

    
    /**
         * Trims the capacity of this <tt>ArrayList</tt> instance to be the
         * list's current size.  An application can use this operation to minimize
         * the storage of an <tt>ArrayList</tt> instance.
         * 
         * 将ArrayList的容量设置为当前size的大小,应用可以使用此方法最小化实例的存储空间
         *
         */
        public void trimToSize() {
            modCount++;
            if (size < elementData.length) {
                elementData = (size == 0)
                  ? EMPTY_ELEMENTDATA
                  : Arrays.copyOf(elementData, size);
            }
        }
        
    
    • 注意:首先需要明确一个概念,ArrayList的size就是ArrayList的元素个数,length是ArrayList申请的内容空间长度。ArrayList每次都会预申请多一点空间,以便添加元素的时候不需要每次都进行扩容操作。此操作修改了列表结构,所以modCount++;
    
        /**
         * Increases the capacity of this <tt>ArrayList</tt> instance, if
         * necessary, to ensure that it can hold at least the number of elements
         * specified by the minimum capacity argument.
         * 
         * 如有必要,增加此ArrayList实例的容量,以确保它至少可以容纳由minCapacity参数指定的元
         * 素数。
         * 
         * @param   minCapacity   the desired minimum capacity
         */
        public void ensureCapacity(int minCapacity) {
            int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
                // any size if not default element table
                ? 0
                // larger than default for default empty table. It's already
                // supposed to be at default size.
                : DEFAULT_CAPACITY;
    
            if (minCapacity > minExpand) {
                ensureExplicitCapacity(minCapacity);
            }
        }
        
        private void ensureCapacityInternal(int minCapacity) {
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
                minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
            }
    
            ensureExplicitCapacity(minCapacity);
        }
     
    
    • ensureCapacity方法中,minExpand 表示最小扩容量, minCapacity表示最小容量,在这里分了两种情况
      此实例 是默认大小的ArrayList,最小扩容量为DEFAULT_CAPACITY 否则为 0;然后比较minCapacity 和 minExpand,如果期望的最小容量比最小扩容量小就不扩容,反之使用minCapacity进行扩容

    ensureCapacityInternal方法,数组容量检查,不够时则进行扩容,只供类内部使用,例如add(),addAll()等方法时会调用此方法进行容量检验,此时minCapacity 通常为size+n

        private void ensureExplicitCapacity(int minCapacity) {
            modCount++;
    
            // overflow-conscious code 
            // 只有最小容量比当前容量大时才会进行扩容
            if (minCapacity - elementData.length > 0)
                grow(minCapacity);
        }
        
        
        /**
         * The maximum size of array to allocate.
         * Some VMs reserve some header words in an array.
         * Attempts to allocate larger arrays may result in
         * OutOfMemoryError: Requested array size exceeds VM limit
         * 
         * 最大存储容量
         */
        private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    
        /**
         * Increases the capacity to ensure that it can hold at least the
         * number of elements specified by the minimum capacity argument.
         *
         * @param minCapacity the desired minimum capacity
         */
        private void grow(int minCapacity) {
            // overflow-conscious code
            int oldCapacity = elementData.length;
            // 先预先扩容1.5倍
            int newCapacity = oldCapacity + (oldCapacity >> 1);
            if (newCapacity - minCapacity < 0)
                newCapacity = minCapacity;
            if (newCapacity - MAX_ARRAY_SIZE > 0)
                // 主要判断 预期最小容量 minCapacity 是否大于 MAX_ARRAY_SIZE
                newCapacity = hugeCapacity(minCapacity);
            // minCapacity is usually close to size, so this is a win:
            elementData = Arrays.copyOf(elementData, newCapacity);
        }
        
        private static int hugeCapacity(int minCapacity) {
            if (minCapacity < 0) // overflow
                throw new OutOfMemoryError();
            return (minCapacity > MAX_ARRAY_SIZE) ?
                Integer.MAX_VALUE :
                MAX_ARRAY_SIZE;
        }
    
    

    总结:

    1. 只有最小容量比当前容量大时才会进行扩容;
    2. 扩容操作时,会预先把容量扩容至自身容量的1.5倍(预扩容)(使用的是oldCapacity + (oldCapacity >> 1)),然后于需要的最小容量minCapacity进行比较,选其最大值;
    3. 最终需要的容量会判断是否超过数组最大容量MAX_ARRAY_SIZE,1.没有超过 ——> 生成新的容量数组;2.超过了 ——> 会进行判断(1.是需要的最小容量超过了MAX_ARRAY_SIZE ——> 最终容量设为Integer.MAX_VALUE; 2.还是预扩容超过了MAX_ARRAY_SIZE ——> 最终容量设为MAX_ARRAY_SIZE) ——> 生成新的容量数组;
    4. 使用最大容量MAX_ARRAY_SIZE时,要注意因为某些VM会在数组中保留一些头字,可能会导致array容量大于VM的limit,最终导致OutOfMemoryError。
    5. minCapacity<0时也会触发MAX_ARRAY_SIZE,但这种情况在我的认知下不会发生,可能是严谨的做法,安全检查;

    普通方法

    
        public boolean contains(Object o) {
        	  // 借用indexOf的方法看是否存在
            return indexOf(o) >= 0;
        }
        
        // 实质就是遍历数组,使用Object的equals方法,返回-1为不存在此元素,lastIndexOf()方法就是反向遍历
        public int indexOf(Object o) {
            if (o == null) {
                for (int i = 0; i < size; i++)
                    if (elementData[i]==null)
                        return i;
            } else {
                for (int i = 0; i < size; i++)
                    if (o.equals(elementData[i]))
                        return i;
            }
            return -1;
        }
        
        
        public boolean add(E e) {
        	 // 检验是否需要扩容
            ensureCapacityInternal(size + 1);  // Increments modCount!!
            // 先在index=size的位置赋值e,再将ArrayList的size增加一位
            elementData[size++] = e;
            return true;
        }
        
        
        /**
         * Inserts the specified element at the specified position in this
         * list. Shifts the element currently at that position (if any) and
         * any subsequent elements to the right (adds one to their indices).
         *
         * @param index index at which the specified element is to be inserted
         * @param element element to be inserted
         * @throws IndexOutOfBoundsException {@inheritDoc}
         */
        public void add(int index, E element) {
            rangeCheckForAdd(index);
    
            ensureCapacityInternal(size + 1);  // Increments modCount!!
            System.arraycopy(elementData, index, elementData, index + 1,
                             size - index);
            elementData[index] = element;
            size++;
        }
        
    

    总结:

    1. add()一个参数直接加在数组最后
    2. add(index, element) 使用System.arraycopy()将index的位置空出来,将值赋值过去。System.arraycopy为 JVM 内部固有方法,它通过手工编写汇编或其他优化方法来进行 Java 数组拷贝,这种方式比起直接在 Java 上进行 for 循环或 clone 是更加高效的。数组越大体现地越明显。
        
        
        /**
         * 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}
         */
        public E remove(int index) {
            rangeCheck(index);
    
            modCount++;
            E oldValue = elementData(index);
    
            int numMoved = size - index - 1;
            if (numMoved > 0)
                System.arraycopy(elementData, index+1, elementData, index,
                                 numMoved);
            
            // 将数组最后一位 置 null,等待垃圾回收机制来回收
            elementData[--size] = null; // clear to let GC do its work
    
            return oldValue;
        }
        
        
        public List<E> subList(int fromIndex, int toIndex) {
            subListRangeCheck(fromIndex, toIndex, size);
            return new SubList(this, 0, fromIndex, toIndex);
        }
        
        private class SubList extends AbstractList<E> implements RandomAccess {
    
    
    

    迭代器(iterator&ListIterator)实现

    ArrayList中的迭代器实现对AbstractList中的迭代器进行了优化

    /**
         * 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>.
         * 
         * 按原有list的顺序返回一个迭代器,遵循fail-fast机制。
         * 
         * 
         * @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> {
            // 后游标:指向下一个元素,等于下一个元素的索引值
            int cursor;       // index of next element to return
            // 前游标:指向当前元素,等于当前元素索引值,-1代表在最开始,还未指向元素
            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();
                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的remove
                    ArrayList.this.remove(lastRet);
                    // remove之后 后游标左移
                    cursor = lastRet;
                    // 重制前游标
                    lastRet = -1;
                    // 并且会使预期修改次数 和 实际修改次数 保持相等
                    expectedModCount = modCount;
                } catch (IndexOutOfBoundsException ex) {
                    throw new ConcurrentModificationException();
                }
            }
    
    
    		  // 检查 实际修改次数 和 预期修改次数是否相等
            final void checkForComodification() {
                if (modCount != expectedModCount)
                    throw new ConcurrentModificationException();
            }
        }
    
    
    • 总结:
      使用迭代器便利的时候,remove元素时,要使用Iterator.remove(),虽然其本质也是调用ArrayList的remove()的方法,只是为了使 modCount,expectedModCount 保持相等,否则会报ConcurrentModificationException()异常
    /**
         * Returns a list iterator over the elements in this list (in proper
         * sequence), starting at the specified position in the list.
         * The specified index indicates the first element that would be
         * returned by an initial call to {@link ListIterator#next next}.
         * An initial call to {@link ListIterator#previous previous} would
         * return the element with the specified index minus one.
         *
         * <p>The returned list iterator is <a href="#fail-fast"><i>fail-fast</i></a>.
         *
         * @throws IndexOutOfBoundsException {@inheritDoc}
         */
        public ListIterator<E> listIterator(int index) {
            if (index < 0 || index > size)
                throw new IndexOutOfBoundsException("Index: "+index);
            return new ListItr(index);
        }
    
        /**
         * Returns a list iterator over the elements in this list (in proper
         * sequence).
         *
         * <p>The returned list iterator is <a href="#fail-fast"><i>fail-fast</i></a>.
         *
         * @see #listIterator(int)
         */
        public ListIterator<E> listIterator() {
            return new ListItr(0);
        }
    
        
        /**
         * An optimized version of AbstractList.ListItr
         */
        private class ListItr extends Itr implements ListIterator<E> {
            ListItr(int index) {
                super();
                cursor = index;
            }
    
            public boolean hasPrevious() {
                return cursor != 0;
            }
    
            public int nextIndex() {
                return cursor;
            }
    
            public int previousIndex() {
                return cursor - 1;
            }
    
            @SuppressWarnings("unchecked")
            public E previous() {
                checkForComodification();
                int i = cursor - 1;
                if (i < 0)
                    throw new NoSuchElementException();
                Object[] elementData = ArrayList.this.elementData;
                if (i >= elementData.length)
                    throw new ConcurrentModificationException();
                cursor = i;
                return (E) elementData[lastRet = i];
            }
            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 set(E e) {
                if (lastRet < 0)
                    throw new IllegalStateException();
                checkForComodification();
    
                try {
                    ArrayList.this.set(lastRet, e);
                } catch (IndexOutOfBoundsException ex) {
                    throw new ConcurrentModificationException();
                }
            }
    
            public void add(E e) {
                checkForComodification();
    
                try {
                    int i = cursor;
                    // 在当前元素的后面加入此元素
                    ArrayList.this.add(i, e);
                    cursor = i + 1;
                    lastRet = -1;
                    // add 操作会导致 modCount++,所以这边要保持相等
                    expectedModCount = modCount;
                } catch (IndexOutOfBoundsException ex) {
                    throw new ConcurrentModificationException();
                }
            }
        }
    
    
    • remove和add操作成功后 lastRet都会重新赋值为-1,并且会执行expectedModCount = modCount操作,以防ConcurrentModificationException;

    • ListItr继承于Itr,增加了previous()等一些方法,最大不同是游标cursor可以向前移动了。

    • 注意:执行过next()方法后再执行previous(),当前元素并没有改变(lastRet没有变),只是cursor进行了了改变,不要进行常规思考 previous()操作就是当前元素向前移动,next()就是当前元素向后移动,准确来说是cursor进行前后移动,进行previous()操作时 lastRet始终等于cursor,next()操作时cursor始终比lastRet大1

    最后声明

      源码版本为JDK1.8,只是对日常使用的基本操作的源码进行了分析,对于1.8的新特性并没有涉及,等将主要集合类源码分析完后,会专门出一篇分析一下1.8中集合的新特性;
      有建议或着问题的,请在文末留言,本人水平有限,有错误或理解偏差,还请各位多多指导和见谅,如若转载,请表明出处;

  • 相关阅读:
    根据时间戳获取年月日时分秒
    除法函数,乘法函数,加法函数,减法函数
    禁止鼠标点右键
    获取cookie,设置cookie,删除cookie
    解决 堆栈 出现的父对象和子对象相关联的问题 (深拷贝)
    团队展示
    用户规格说明书——结对编程
    测试与优化——结对编程
    程序开发——结对编程
    程序开发初体验
  • 原文地址:https://www.cnblogs.com/wangshiwen/p/10215118.html
Copyright © 2011-2022 走看看