ArrayList工做原理

1.前言

List接口中,重要的兩個實現是ArrayList和LinkedList,其中ArrayList又比LinkedList經常使用。這是由於ArrayList的讀取性能遠遠高於LinkedList。本篇博文將介紹ArrayList,稍後介紹LinkedList及其二者的區別。java

2.ArrayList簡介

    2.1 ArrayList是什麼

Resizable-array implementation of the List interface.
Implements all optional list operations, and permits all elements, including null.
In addition to implementing the List interface,this class provides methods to manipulate the size of the array that is used internally to store the list.

        ArrayList是實現了List接口的、大小可變的數組隊列。可以實現全部List接口的可選操做,並容許包括 null 在內的全部元素。除了實現 List 接口外,此類還提供一些方法來存儲列表的數組的大小。(此類大體上等同於 Vector 類,除了此類是不一樣步的。)--JDK API翻譯(可能不是很精確,請諒解)算法

        從API的定義中能夠看出,ArrayList是一個數組隊列,至關於動態數組。與數組相比,可以動態增加。api

    2.2 ArrayList的數據結構

 

 

 

 

 

 

 

 

 

java.util 
類 ArrayList<E>
java.lang.Object
  - java.util.AbstractCollection<E>
      - java.util.AbstractList<E>
          - java.util.ArrayList<E>
全部已實現的接口:
Serializable, Cloneable, Iterable<E>, Collection<E>, List<E>, RandomAccess

    ArrayList繼承了AbstractList,實現了List,它是一個數組隊列,提供了相關的增、改、刪數組

查等功能。安全

    ArrayList實現了RandomAccess接口,提供了隨機訪問功能。RandomAccess是List 實現所使用的標記接口,用來代表其支持快速(一般是固定時間)隨機訪問。此接口的主要目的是容許通常的算法更改其行爲,從而在將其應用到隨機或連續訪問列表時能提供良好的性能。(來自JDk API翻譯)數據結構

    ArrayList實現了Cloneable接口,提供了克隆功能多線程

    ArrayList實現了java.io.Serializable藉口,這意味着ArrayList支持序列化。併發

    注意:ArrayList是非線程安全的!因此,建議在多線程程序中避免使用ArrayList,可使用Vector或者CopyOnWriteArrayList。(這兩個類的用法及其實現,在稍後的博文中將被介紹)app

3.源碼解析ArrayList    

    3.1 ArrayList的重要屬性

package java.util;

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
//序列版本號
    private static final long serialVersionUID = 8683452581122892189L;

    /**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer.
	 * 數組緩衝區中的數組的元素存儲,即保存ArrayList中數據的數組 
     */
    private transient Object[] elementData;

    /**
     * The size of the ArrayList (the number of elements it contains).
     * ArrayList中實際數據的數量
     * @serial
     */
    private int size;
}

       從源碼中看到ArrayList有2個重要對象dom

  • elementData是「Object[]類型的數組」,它保存了添加到ArrayList中的元素。實際上,elementData是個動態數組,咱們經過帶初始容量的構造器來執行構建數組。若是不含初始化容量的構造器,elementData的容量是10.elementData數組的大小會根據ArrayList容量的增加而動態增加。可是建議在添加大量元素前,應用程序可使用 ensureCapacity 操做來增長 ArrayList 實例的容量。這能夠減小遞增式再分配的數量。構造器的內容,詳見3.2 ArrayList的構造函數。具體的增加方式,請參照3.3 ArrayList的重要方法中的ensureCapacity()函數解析
  • size 是動態數組的實際大小

     3.2 ArrayList的構造函數

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    /**
     * Constructs an empty list with the specified initial capacity.
     *
     * 構造一個具備指定初始容量的空列表。
     */
    public ArrayList(int initialCapacity) {
	super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
	//新建一個數組,大小爲入參值										   
	this.elementData = new Object[initialCapacity];
    }

    /**
     * Constructs an empty list with an initial capacity of ten.
     * ArrayList的無參構造器, 構造一個初始容量爲 10 的空列表
     */
    public ArrayList() {
	this(10);
    }

    /**
     * Constructs a list containing the elements of the specified
     * collection, in the order they are returned by the collection's
     * iterator.
     *
     * 構造一個包含指定 collection 的元素的列表,這些元素是按照該 collection 的迭代器返回它們的順序排列的。     */
    public ArrayList(Collection<? extends E> c) {
	elementData = c.toArray();
	size = elementData.length;
	// c.toArray might (incorrectly) not return Object[] (see 6260652)
	if (elementData.getClass() != Object[].class)
	//Arrays.copyOf方法:數組複製
	    elementData = Arrays.copyOf(elementData, size, Object[].class);
    }
}

    3.3 ArrayList的重要方法

        3.3.1 增長ArrayList容量的方法ensureCapacity()

            當ArrayList容量不足以容納所有元素時,ArrayList會從新設置容量:新的容量 = (原始容量 X 3) / 2 + 1。再次說明在添加大量元素前,應用程序應該使用 ensureCapacity 操做來增長 ArrayList 實例的容量。以減小遞增式再分配的數量。

/**
     * 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 實例的容量,以確保它至少可以容納最小容量參數所指定的元素數。
     */
    public void ensureCapacity(int minCapacity) {
	//將修改統計計數 +1
	modCount++;
	int oldCapacity = elementData.length;
	//若是傳入最小容量大於原來數組的長度,就增長arrayList容量
	//新的容量 = (原始容量 * 3)/2 + 1
	if (minCapacity > oldCapacity) {
	    Object oldData[] = elementData;
	    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);
	}
    }

        3.3.2 最小化 ArrayList 實例的存儲量方法trimToSize()

        將此 ArrayList 實例的容量調整爲列表的當前大小。應用程序可使用此操做來最小化 ArrayList 實例的存儲量。

/**
     * Trims the capacity of this ArrayList instance to be the list's current size.
     * 將此 ArrayList 實例的容量調整爲列表的當前大小,也即將當前容量值設置爲實際元素個數
     */
    public void trimToSize() {
	modCount++;
	int oldCapacity = elementData.length;
	if (size < oldCapacity) {
            elementData = Arrays.copyOf(elementData, size);
	}
    }

        3.3.3 添加元素add()

        將指定的元素添加到此列表的尾部。在調整數組大小以前要判斷是否須要調整arrayList容量

/**
     * Appends the specified element to the end of this list.
     *
     * 添加元素e
     */
    public boolean add(E e) {
	//肯定ArrayList容量大小
	ensureCapacity(size + 1);  // Increments modCount!!
	//添加e到ArrayList尾部
	elementData[size++] = e;
	return true;
    }

        3.3.4 檢查是否含有指定元素的方法contains(object o)

/**
     * Returns true if this list contains the specified element.
     * 返回ArrayList是否包含Object(o)
     */
    public boolean contains(Object o) {
	return indexOf(o) >= 0;
    }

    /**
     * Returns the index of the first occurrence of the specified element
     * in this list, or -1 if this list does not contain the element.
     * 正向查找,返回元素的索引值,若是元素不存在集合中,就返回-1
     */
    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;
    }

        3.3.5 其他方法詳見源碼(帶翻譯)

package java.util;

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    // 正向查找,返回元素的索引值
    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 int lastIndexOf(Object o) {
        if (o == null) {
            for (int i = size-1; i >= 0; i--)
            if (elementData[i]==null)
                return i;
        } else {
            for (int i = size-1; i >= 0; i--)
            if (o.equals(elementData[i]))
                return i;
        }
        return -1;
    }

    // 反向查找(從數組末尾向開始查找),返回元素(o)的索引值
    public int lastIndexOf(Object o) {
        if (o == null) {
            for (int i = size-1; i >= 0; i--)
            if (elementData[i]==null)
                return i;
        } else {
            for (int i = size-1; i >= 0; i--)
            if (o.equals(elementData[i]))
                return i;
        }
        return -1;
    }
 

    // 返回ArrayList的Object數組
    public Object[] toArray() {
        return Arrays.copyOf(elementData, size);
    }

    // 返回ArrayList的模板數組。所謂模板數組,便可以將T設爲任意的數據類型
    public <T> T[] toArray(T[] a) {
        // 若數組a的大小 < ArrayList的元素個數;
        // 則新建一個T[]數組,數組大小是「ArrayList的元素個數」,並將「ArrayList」所有拷貝到新數組中
        if (a.length < size)
            return (T[]) Arrays.copyOf(elementData, size, a.getClass());

        // 若數組a的大小 >= ArrayList的元素個數;
        // 則將ArrayList的所有元素都拷貝到數組a中。
        System.arraycopy(elementData, 0, a, 0, size);
        if (a.length > size)
            a[size] = null;
        return a;
    }

    // 設置index位置的值爲element
    public E set(int index, E element) {
        RangeCheck(index);

        E oldValue = (E) elementData[index];
        elementData[index] = element;
        return oldValue;
    }

    // 將e添加到ArrayList的指定位置
    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++;
    }

    // 刪除ArrayList指定位置的元素
    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;
    }

    // 刪除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;
    }


    // 快速刪除第index個元素
    private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        // 從"index+1"開始,用後面的元素替換前面的元素。
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        // 將最後一個元素設爲null
        elementData[--size] = null; // Let gc do its work
    }

    // 刪除元素
    public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
            if (elementData[index] == null) {
                fastRemove(index);
            return true;
            }
        } else {
            // 便利ArrayList,找到「元素o」,則刪除,並返回true。
            for (int index = 0; index < size; index++)
            if (o.equals(elementData[index])) {
                fastRemove(index);
            return true;
            }
        }
        return false;
    }

    // 清空ArrayList,將所有的元素設爲null
    public void clear() {
        modCount++;

        for (int i = 0; i < size; i++)
            elementData[i] = null;

        size = 0;
    }

    // 將集合c追加到ArrayList中
    public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacity(size + numNew);  // Increments modCount
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
        return numNew != 0;
    }

    // 從index位置開始,將集合c添加到ArrayList
    public boolean addAll(int index, Collection<? extends E> c) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(
            "Index: " + index + ", Size: " + size);

        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacity(size + numNew);  // Increments modCount

        int numMoved = size - index;
        if (numMoved > 0)
            System.arraycopy(elementData, index, elementData, index + numNew,
                 numMoved);

        System.arraycopy(a, 0, elementData, index, numNew);
        size += numNew;
        return numNew != 0;
    }

    // 刪除fromIndex到toIndex之間的所有元素。
    protected void removeRange(int fromIndex, int toIndex) {
    modCount++;
    int numMoved = size - toIndex;
        System.arraycopy(elementData, toIndex, elementData, fromIndex,
                         numMoved);

    // Let gc do its work
    int newSize = size - (toIndex-fromIndex);
    while (size != newSize)
        elementData[--size] = null;
    }

    // 克隆函數
    public Object clone() {
        try {
            ArrayList<E> v = (ArrayList<E>) super.clone();
            // 將當前ArrayList的所有元素拷貝到v中
            v.elementData = Arrays.copyOf(elementData, size);
            v.modCount = 0;
            return v;
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError();
        }
    }
}

4. ArrayList遍歷方式

    集合操做中最多見的就是遍歷操做了,ArrayList支持3種遍歷方式:迭代器遍歷、隨機訪問和for循環|(foreach循環)

    4.1 迭代器遍歷,即經過Iterator去遍歷(效率低下)

Integer value = null;
Iterator iter = list.iterator();
while (iter.hasNext()) {
    value = (Integer)iter.next();
}

    4.2 隨機訪問遍歷,經過索引值去遍歷(效率高)

Integer value = null;
int size = list.size();
for (int i=0; i<size; i++) {
    value = (Integer)list.get(i);        
}

    4.3 for循環遍歷,效率介於兩者之間

Integer value = null;
for (Integer integ:list) {
    value = integ;
}

    4.4 三種遍歷效率比對

package com.my.mobile;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class TestArrayList {
	public static void main(String[] args) {
		TestArrayList t = new TestArrayList();
		List<Data> initArrayList = t.initArrayList(100000);
		testiterator(initArrayList);
		testRandomAccess(initArrayList);
		testforeach(initArrayList);
		
	}
	/**
	 * 測試foreach循環遍歷ArrayList性能
	 * @param initArrayList
	 * */
	private static void testforeach(List<Data> initArrayList) {
		// TODO Auto-generated method stub
		long starttime = System.currentTimeMillis();
		for (Data data : initArrayList) {
//			System.out.println(data.toString());
		}
		System.out.println("foreach遍歷完畢,耗時["+(System.currentTimeMillis() - starttime)+"]ms");
	}

	private static void testRandomAccess(List<Data> initArrayList) {
		// TODO Auto-generated method stub
		long starttime = System.currentTimeMillis();
		for(int i = 0;i<initArrayList.size();i++){
			Data data = initArrayList.get(i);
//			System.out.println(data.toString());
		}
		System.out.println("randomAccess遍歷完畢,耗時["+(System.currentTimeMillis() - starttime)+"]ms");
	}

	private static void testiterator(List<Data> initArrayList) {
		// TODO Auto-generated method stub
		long starttime = System.currentTimeMillis();
		for(Iterator<Data> iter = initArrayList.iterator();iter.hasNext();){
			Data data = iter.next();
//			System.out.println(data.toString());
		}
		System.out.println("iterator遍歷完畢,耗時["+(System.currentTimeMillis() - starttime)+"]ms");
	}

	private List<Data> initArrayList(int listSize){
		Data data = new Data();
		List<Data> arrayList = new ArrayList<Data>();
		for(int i = 0;i<listSize;i++){
			try {
				TestArrayList.Data data_clone = data.clone();
				data_clone.setId(i+"");
				data_clone.setName("我是第"+i+"個酷酷的酷");
				data_clone.setUser("kucs");
				arrayList.add(data_clone);
			} catch (CloneNotSupportedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return arrayList;
	}
	class Data implements Cloneable{
		private String id;
		private String name;
		private String user;

		public Data() {
			super();
			// TODO Auto-generated constructor stub
		}

		public String getId() {
			return id;
		}

		public void setId(String id) {
			this.id = id;
		}

		public String getName() {
			return name;
		}

		public void setName(String name) {
			this.name = name;
		}

		public String getUser() {
			return user;
		}

		public void setUser(String user) {
			this.user = user;
		}

		@Override
		public String toString() {
			return "Data [id=" + id + ", name=" + name + ", user=" + user + "]";
		}

		@Override
		protected Data clone() throws CloneNotSupportedException {
			// TODO Auto-generated method stub
			return (Data)super.clone();
		}
		
	}
}

運行結果:

iterator遍歷完畢,耗時[34]ms
randomAccess遍歷完畢,耗時[12]ms
foreach遍歷完畢,耗時[14]ms

    經此實驗,遍歷ArrayList時,使用隨機訪問,遍歷比較快。

5.ArrayList總結

  •     ArrayList是實現了List接口的、大小可變的數組隊列。可以實現全部List可選操做,並容許包括null在內的全部元素。除了實現List接口外,此類還提供一些方法來操做內部用來存儲列表的數組的大小。(此類大體上等同於 Vector 類,除了此類是不一樣步的。)
  • ArrayList的size、isEmpty、get、set、iterator和listIterator操做都以固定時間運行。add操做以分攤的固定時間運行,也就是說,添加n個元素須要O(n)時間。其餘全部操做都以線性時間運行。與用於LinkedList實現的常數因子相比,此實現的常數因子較低。
  • 每一個ArrayList實例都有一個容量。該容量是指用來存儲列表元素的數組的大小。它老是至少等於列表的大小。隨着向ArrayList中不斷添加元素,其容量也自動增加。並未指定增加策略的細節,由於這不僅是添加元素會帶來分攤固定時間開銷那樣簡單。在添加大量元素前,應用程序可使用ensureCapacity操做來增長ArrayList實例的容量。這能夠減小遞增式再分配的數量。
  • 注意,此實現不是同步的。若是多個線程同時訪問一個ArrayList實例,而其中至少一個線程從結構上修改了列表,那麼它必須保持外部同步。(結構上的修改是指任何添加或刪除一個或多個元素的操做,或者顯式調整底層數組的大小;僅僅設置元素的值不是結構上的修改。)這通常經過對天然封裝該列表的對象進行同步操做來完成。若是不存在這樣的對象,則應該使用Collections.synchronizedList方法將該列表「包裝」起來。這最好在建立時完成,以防止意外對列表進行不一樣步的訪問。儘可能避免在併發訪問中使用ArrayList。可使用同步的Vector或者CopyOnWriteList集合代替ArrayList
相關文章
相關標籤/搜索