ArrayList源码学习

ArrayList就是动态数组,用MSDN中的说法,就是Array的复杂版本,它提供了动态的增加和减少元素,实现了ICollection和IList接口,灵活的设置数组的大小等好处。

ArrayList的属性说明:

  • private transient Object[] 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.

  • private int size;

        The size of the ArrayList (the number of elements it contains).

  •  protected transient int modCount = 0;

         The number of times this list has been <i>structurally modified</i>

         已从结构上修改 此列表的次数。从结构上修改是指更改列表的大小,或者打乱列表,从而使正在进行的迭代产生错误的结果。

         此属性继承自AbstractList。

疑问一:ArrayList是一个动态数组,如何实现数组的动态呢?

  1. 在创建(new)的时候,默认会将 elementData属性设置为容量为10的数组,或者按照指定大小设置elementData的初始容量。具体代码如下:
    /**
         * Constructs an empty list with an initial capacity of ten.
         */
        public ArrayList() {
    	this(10);
        }
    
     
       
       /**
         * Constructs an empty list with the specified initial capacity.
         *
         * @param   initialCapacity   the initial capacity of the list
         * @exception IllegalArgumentException if the specified initial capacity
         *            is negative
         */
        public ArrayList(int initialCapacity) {
    	super();
            if (initialCapacity < 0)
                throw new IllegalArgumentException("Illegal Capacity: "+
                                                   initialCapacity);
    	this.elementData = new Object[initialCapacity];
        }
     
  2. 在向ArrayList增加元素时,比如add方法时,首先会调用方法
    public void ensureCapacity(int minCapacity)
    如有必要,增加此 ArrayList 实例的容量,以确保它至少能够容纳最小容量参数所指定的元素数。
        /**
         * Appends the specified element to the end of this list.
         *
         * @param e element to be appended to this list
         * @return <tt>true</tt> (as specified by {@link Collection#add})
         */
        public boolean add(E e) {
    	ensureCapacity(size + 1);  // Increments modCount!!
    	elementData[size++] = e;
    	return true;
        }
    
     
    /**
         * 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.
         *
         * @param   minCapacity   the desired minimum capacity
         */
        public void ensureCapacity(int minCapacity) {
    	modCount++;
    	int oldCapacity = elementData.length;
                      //判断要求的容量大于elementData数组现在的容量
    	if (minCapacity > oldCapacity) {
    	    Object oldData[] = elementData;
                          //增加容量为现有容量的1/2+1,为什么加1,考虑原容量为0或者1的情况?
    	    int newCapacity = (oldCapacity * 3)/2 + 1;
                          //如果自动扩容后依然小于要求容量,则按要求容量作为新数组容量
        	    if (newCapacity < minCapacity)
    		newCapacity = minCapacity;
                // minCapacity is usually close to size, so this is a win:
                //复制原数据,生成新的扩容数组
                elementData = Arrays.copyOf(elementData, newCapacity);
    	}
        }
     

疑问二:继承属性modCount的作用:

表示已从结构上修改 此列表的次数。从结构上修改是指更改列表的大小,或者打乱列表,从而使正在进行的迭代产生错误的结果。

此属性的默认值是0,当结构上修改如向列表中增加、减少元素时,执行modCount++,可参考以上的add方法。在其迭代器Iterator调用时,或者执行方法writeObject时,会检查modCount的值是否发生变更,如果是,则抛出异常ConcurrentModificationExceptions。代码如下:

ArrayList的获取迭代器的方法继承自父类AbstractList

 public Iterator<E> iterator() {
	return new Itr();
    }

 需要时创建新的迭代器,即创建实现了Iterator接口的AbstractList的内部类Itr 。其中属性在创建时设置。

/**
  The modCount value that the iterator believes that the backing
  
* List should have.  If this expectation is violated, the iterator
  
* has detected concurrent modification.
  
**/
int expectedModCount = modCount;

 在调用Itr其中的next和remove的时候,会检查ArrayList的结构是否发生变更,方法checkForComodification()

         public E next() {
            checkForComodification();
	    try {
		E next = get(cursor);
		lastRet = cursor++;
		return next;
	    } catch (IndexOutOfBoundsException e) {
		checkForComodification();
		throw new NoSuchElementException();
	    }
          }

 如果方法变更,则抛出异常。

  final void checkForComodification() {
	    if (modCount != expectedModCount)
		throw new ConcurrentModificationException();
	}
    }

 疑问三:ArrayList的数组属性elementData前面的修饰符transient涉及序列化。

序列化有2种方式:
A、只是实现了Serializable接口。

  序列化时,调用java.io.ObjectOutputStream的defaultWriteObject方法,将对象序列化。

注意:此时transient修饰的字段,不会被序列化。

B、实现了Serializable接口,同时提供了writeObject方法。

  序列化时,会调用该类的writeObject方法。而不是java.io.ObjectOutputStream的defaultWriteObject方法。

注意:此时transient修饰的字段,是否会被序列化,取决于writeObject。


ArrayList的源码:

 private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException{
	// Write out element count, and any hidden stuff
	int expectedModCount = modCount;
	s.defaultWriteObject();

        // Write out array length
        s.writeInt(elementData.length);

	// Write out all elements in the proper order.
	for (int i=0; i<size; i++)
            s.writeObject(elementData[i]);

	if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }

    }

ArrayList实现了java.io.Serializable,复写了以下2个方法:

private void writeObject(java.io.ObjectOutputStream s)

private void readObject(java.io.ObjectInputStream s)

从源码中可以看出,先调用java.io.ObjectOutputStream的defaultWriteObject方法,进行默认的序列化操作,此时transient修饰的字段,没有被序列化。

接着:for循环,将数组中的元素写出,序列化。而数组中的元素正是transient。

疑问四:查找元素和添加删除元素的效率?

在动态数组中,如果我们要在某一个位置添加或者删除一个元素,剩下的每个元素都要相应地往前或往后移动。如果该动态数组中的元素很多,那么,每当我们添加或删除一个元素后,需要移动的元素就非常多,因此,效率就比较低。除了在动态数组末尾增加元素,这样不需要移动元素。

/**
     * 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) {
	if (index > size || index < 0)
	    throw new IndexOutOfBoundsException(
		"Index: "+index+", Size: "+size);

	ensureCapacity(size+1);  // Increments modCount!!
	System.arraycopy(elementData, index, elementData, index + 1,
			 size - index);
	elementData[index] = element;
	size++;
    }
/**
     * 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 = (E) elementData[index];

	int numMoved = size - index - 1;
	if (numMoved > 0)
	    System.arraycopy(elementData, index+1, elementData, index,
			     numMoved);
	elementData[--size] = null; // Let gc do its work

	return oldValue;
    }

System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length)

从指定源数组中复制一个数组,复制从指定的位置开始,到目标数组的指定位置结束。

猜你喜欢

转载自qiyanb.iteye.com/blog/2199530