前面的學習中咱們知道,集合中存放了咱們隨時可能須要使用到的對象,集合類提供增刪改等基本接口。就比如盛東西的容器,從某種意義上說,數組也是一種容器。可是數組太簡陋了,在使用以前必須指定長度,不能動態擴容,沒有方便的增刪改接口,沒有迭代器。惟一的優勢就是簡單直接。所以Java爲了彌補數組的不足,提供了豐富的集合類。今天咱們將對集合類中的ArrayList進行詳細的學習。html
在學習ArrayList以前,咱們得先對List接口進行一個簡單的瞭解。List接口定義了列表類。在List的源碼中,有很是詳細的註釋。這裏給你們看一下注釋的第一段:java
/**
* An ordered collection (also known as a <i>sequence</i>). The user of this
* interface has precise control over where in the list each element is
* inserted. The user can access elements by their integer index (position in
* the list), and search for elements in the list.<p>
*
* Unlike sets, lists typically allow duplicate elements.
*/
複製代碼
這一段說明了List這個接口的做用,是一個有序的容器,使用這個接口的人,能夠精確地控制數據存放在列表的什麼位置,用戶還能夠經過一個整型的索引去檢索容器裏的某個元素,還有,List和Set最大的不一樣就是, List中容許元素是重複的。你們在學習過程當中能夠多讀一讀這些註釋。
List繼承自Collection,和Collection比較,List 還添加了如下操做方法:git
以數組實現。節約空間,但數組有容量限制。超出限制時會增長50%容量,用System.arraycopy()複製到新的數組,所以最好能給出數組大小的預估值。默認第一次插入元素時建立大小爲10的數組。
按照數組索引訪問元素:get(int index)/set(int index)的性能很高,這是數組的優點。直接在數組末尾加入元素:add(e)的性能也高,但若是按索引插入、刪除元素:add(i,e)、remove(i)、remove(e),則要用System.arraycopy()來移動部分受影響的元素,性能就變差了,這是數組的劣勢。
ArrayList不是線程安全的,只能在單線程環境下,多線程環境下能夠考慮用Collections.synchronizedList(List list)方法返回一個線程安全的ArrayList對象,也可使用concurrent併發包下的CopyOnWriteArrayList類。
ArrayList實現了Serializable接口,所以它支持序列化,可以經過序列化傳輸,實現了RandomAccess接口,支持快速隨機訪問,實際上根據源碼咱們知道就是經過索引序號進行快速訪問,實現了Cloneable接口,能被克隆。github
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
//序列號版本
private static final long serialVersionUID = 8683452581122892189L;
//默認初始容量爲10
private static final int DEFAULT_CAPACITY = 10;
//共享空數組實例用於空實例
private static final Object[] EMPTY_ELEMENTDATA = {};
//共享空數組實例用於默認大小的空實例
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//ArrayList基於該數組實現,用該數組保存數據
transient Object[] elementData;
//ArrayList中實際數據的數量
private int size;
//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);
}
}
//ArrayList無參構造方法。當元素第一次被加入時,擴容爲默認大小10
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//建立一個包含Collection的ArrayList
public ArrayList(Collection<? extends E> c) {
//調用toArray()方法把collection轉換爲數組
elementData = c.toArray();
//將轉換後的 Object[] 長度賦值給當前 ArrayList 的 size,並判斷是否爲 0
if ((size = elementData.length) != 0) {
if (elementData.getClass() != Object[].class)
// 若 c.toArray() 返回的數組類型不是 Object[],則利用 Arrays.copyOf(); 來構造一個大小爲 size 的 Object[] 數組
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
//換成空數組
this.elementData = EMPTY_ELEMENTDATA;
}
}
//將當前容量值設爲實際元素個數
public void trimToSize() {
modCount++;
if (size < elementData.length) {
//調整數組緩衝區elementData,變爲實際存儲大小Arrays.copyOf(elementData,size)
elementData = (size == 0)? EMPTY_ELEMENTDATA : Arrays.copyOf(elementData, size);
}
}
//肯定ArrayList的容量。(指定最小容量)
public void ensureCapacity(int minCapacity) {
//最小擴容,默認是10
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) ? 0: DEFAULT_CAPACITY;
//若是用戶指定的最小容量>最小擴容10,就以用戶指定爲準
if (minCapacity > minExpand) {
//肯定明確的容量
ensureExplicitCapacity(minCapacity);
}
}
//私有方法:肯定ArrayList的容量大小
private void ensureCapacityInternal(int minCapacity) {
//確保容量大小>=默認大小
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
//肯定明確容量
ensureExplicitCapacity(minCapacity);
}
//肯定ArrayList的具體大小
private void ensureExplicitCapacity(int minCapacity) {
//將「修改統計數」+1,該變量主要是用來實現fail-fast機制
modCount++;
// 超出了數組可容納的長度,須要進行動態擴展
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
//要分配的數組的最大大小
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
//擴容。確保至少能容納最小容量參數指定的元素個數。
//這是動態擴容的精髓,ArrayList的奧祕盡收眼底
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
//獲得數組的舊容量,進行oldCapacity + (oldCapacity >> 1),將oldCapacity右移一位,至關於oldCapacity/2
//這樣的結果即是將新數組的容量擴展到原來數組的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
//判斷新數組的容量夠不夠,夠了就直接使用這個大小建立新的數組
//不夠就將數組大小設置爲須要的大小
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//在判斷有沒有超過上面的最大容量限制,超出限制就調hugeCapacity()方法進行處理
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
//將原來數組的值copy新數組中去,ArrayList的引用指向新數組
//這兒會新建立數組,若是數據量很大,重複的建立數組,會影響效率
//所以最好在合適的時候經過構造方法指定默認的capaticy大小
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;
}
//返回ArrayList的實際大小
public int size() {
return size;
}
//返回ArrayList是否爲空
public boolean isEmpty() {
return size == 0;
}
//ArrayList是否包含Object(o)
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
//正向查找,返回元素的索引值
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;
}
//克隆函數
//對拷貝出來的ArrayList對象的操做,不會影響原來的ArrayList
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
//將當前ArrayList的所有元素拷貝到v中
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
throw new InternalError(e);
}
}
//返回ArrayList的Object數組
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
//返回ArrayList元素組成的數組
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
//若是數組a的大小 < ArrayList的元素個數
if (a.length < size)
//就新建一個T[]數組,數組大小爲ArrayList的元素個數,而後將ArrayList所有拷貝到新數組中
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
//若是數組a的大小 >= ArrayList的元素個數
//就將ArrayList的所有元素都拷貝到數組中
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
//獲取index位置的元素值
public E get(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
return (E) elementData[index];
}
//設置index位置的值爲element
public E set(int index, E element) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
E oldValue = (E) elementData[index];
elementData[index] = element;
return oldValue;
}
//添加元素
//根據傳入的最小須要容量minCapacity來和數組的容量長度對比,若minCapactity大於或等於數組容量,則須要進行擴容。
public boolean add(E e) {
//肯定ArrayList的容量大小
ensureCapacityInternal(size + 1); // Increments modCount!!
//添加e到ArrayList中
elementData[size++] = e;
return true;
}
//將e添加到ArrayList的指定位置
public void add(int index, E element) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
//刪除ArrayList指定位置的元素
public E remove(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(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; // clear to 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 {
//遍歷ArrayList,找到元素o,則刪除,並返回true.
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; // clear to let GC do its work
}
//清空ArrayList,將所有的元素設爲null
public void clear() {
modCount++;
// clear to let GC do its work
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;
ensureCapacityInternal(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(outOfBoundsMsg(index));
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(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) {
if (toIndex < fromIndex) {
throw new IndexOutOfBoundsException("toIndex < fromIndex");
}
modCount++;
int numMoved = size - toIndex;
System.arraycopy(elementData, toIndex, elementData, fromIndex,
numMoved);
// clear to let GC do its work
int newSize = size - (toIndex-fromIndex);
for (int i = newSize; i < size; i++) {
elementData[i] = null;
}
size = newSize;
}
private String outOfBoundsMsg(int index) {
return "Index: "+index+", Size: "+size;
}
//刪除ArrayList中包含Collection c中的的元素
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, false);
}
//保留ArrayList中包含Collection c中的的元素
public boolean retainAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, true);
}
//批量刪除
private boolean batchRemove(Collection<?> c, boolean complement) {
final Object[] elementData = this.elementData;
int r = 0, w = 0;
boolean modified = false;
try {
for (; r < size; r++)
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
} finally {
// Preserve behavioral compatibility with AbstractCollection,
// even if c.contains() throws.
if (r != size) {
System.arraycopy(elementData, r,elementData,w,size - r);
w += size - r;
}
if (w != size) {
// clear to let GC do its work
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
}
}
return modified;
}
//java.io.Serializable的寫入方法
//將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();
// 寫入數組的容量
s.writeInt(size);
// 寫入數組的每個元素
for (int i=0; i<size; i++) {
s.writeObject(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
//java.io.Serializable的讀取方法:根據寫入方式讀出
//先將ArrayList的容量讀出,再將全部的元素值讀出
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
elementData = EMPTY_ELEMENTDATA;
// Read in size, and any hidden stuff
s.defaultReadObject();
// 從輸入流中讀取ArrayList的容量
s.readInt(); // ignored
if (size > 0) {
// be like clone(), allocate array based upon size not capacity
ensureCapacityInternal(size);
Object[] a = elementData;
//從輸入流中將全部的元素值讀出
for (int i=0; i<size; i++) {
a[i] = s.readObject();
}
}
}
//返回一個ListIterator
public ListIterator<E> listIterator(int index) {
if (index < 0 || index > size)
throw new IndexOutOfBoundsException("Index: "+index);
return new ListItr(index);
}
//返回一個ListIterator迭代器,該迭代器是fail-fast機制的
public ListIterator<E> listIterator() {
return new ListItr(0);
}
//返回一個Iterator迭代器,該迭代器是fail-fast機制的
public Iterator<E> iterator() {
return new Itr();
}
//AbstractList.Itr的優化版本,不作深究
private class Itr implements Iterator<E> {
···
}
/**
* AbAbstractList.ListItr 的優化版本
* ListIterator與普通的Iterator的區別:
* 它能夠進行雙向移動,而普通的迭代器只能單向移動
* 它能夠添加元素(有add()方法),然後者不行
*/
private class ListItr extends Itr implements ListIterator<E> {
...
}
//獲取從fromIndex到toIndex之間的子集合(左閉右開區間)
/**
* 若是fromIndex == toIndex,則返回空集合
* 對該子集合的操做,會影響原有集合
* 當調用了subList()後,若對原集合進行刪除操做(刪除subList中的首個元素)時,會拋出異常
* 該本身支持全部的集合操做
*/
public List<E> subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size); //合法性檢查
return new SubList(this, 0, fromIndex, toIndex);
}
static void subListRangeCheck(int fromIndex, int toIndex, int size) {
//越界檢查
if (fromIndex < 0)
throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
if (toIndex > size)
throw new IndexOutOfBoundsException("toIndex = " + toIndex);
//非法參數檢查
if (fromIndex > toIndex)
throw new IllegalArgumentException("fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")");
}
//嵌套內部類,實現了RandomAccess,提供快速隨機訪問特性
private class SubList extends AbstractList<E> implements RandomAccess {
...
}
//1.8方法,用於函數式編程
@Override
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
final int expectedModCount = modCount;
@SuppressWarnings("unchecked")
final E[] elementData = (E[]) this.elementData;
final int size = this.size;
for (int i=0; modCount == expectedModCount && i < size; i++) {
action.accept(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
//獲取一個分割器,java8開始提供
@Override
public Spliterator<E> spliterator() {
return new ArrayListSpliterator<>(this, 0, -1, 0);
}
//基於索引的、二分的、懶加載的分割器
static final class ArrayListSpliterator<E> implements Spliterator<E> {
...
}
//移除集合中知足給定條件的全部元素 1.8新增
@Override
public boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter);
int removeCount = 0;
final BitSet removeSet = new BitSet(size);
final int expectedModCount = modCount;
final int size = this.size;
for (int i=0; modCount == expectedModCount && i < size; i++) {
@SuppressWarnings("unchecked")
final E element = (E) elementData[i];
if (filter.test(element)) {
removeSet.set(i);
removeCount++;
}
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
final boolean anyToRemove = removeCount > 0;
if (anyToRemove) {
final int newSize = size - removeCount;
for (int i=0, j=0; (i < size) && (j < newSize); i++, j++) {
i = removeSet.nextClearBit(i);
elementData[j] = elementData[i];
}
for (int k=newSize; k < size; k++) {
elementData[k] = null; // Let gc do its work
}
this.size = newSize;
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
return anyToRemove;
}
//1.8新增 替換方法
@Override
@SuppressWarnings("unchecked")
public void replaceAll(UnaryOperator<E> operator) {
Objects.requireNonNull(operator);
final int expectedModCount = modCount;
final int size = this.size;
for (int i=0; modCount == expectedModCount && i < size; i++) {
elementData[i] = operator.apply((E) elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
//排序
@Override
@SuppressWarnings("unchecked")
public void sort(Comparator<? super E> c) {
final int expectedModCount = modCount;
Arrays.sort((E[]) elementData, 0, size, c);
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
}
複製代碼
在對ArrayList進行源碼分析的時候,發現ArrayList有不少須要考慮的點。基本上,剛剛的代碼裏都寫上了,這裏咱們再作個總結:
1.ArrayList是基於數組實現的,它的內存儲元素的數組爲 elementData;elementData的聲明爲:transient Object[] elementData;
2.ArrayList中EMPTY_ELEMENTDATA和DEFAULTCAPACITY_EMPTY_ELEMENTDATA的使用;這兩個常量,使用場景不一樣。前者是用在用戶經過ArrayList(int initialCapacity)該構造方法直接指定初始容量爲0時,後者是用戶直接使用無參構造建立ArrayList時。
3.ArrayList默認容量爲10。調用無參構造新建一個ArrayList時,它的elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA, 當第一次使用 add() 添加元素時,ArrayList的容量會爲 10。
4.ArrayList的擴容計算爲 newCapacity = oldCapacity + (oldCapacity >> 1);且擴容並不是是無限制的,有內存限制,虛擬機限制。 5.ArrayList的toArray()方法和subList()方法,在源數據和子數據之間的區別;編程
public String toString() {
Iterator<E> it = iterator();
if (! it.hasNext())
return "[]";
StringBuilder sb = new StringBuilder();
sb.append('[');
for (;;) {
E e = it.next();
sb.append(e == this ? "(this Collection)" : e);
if (! it.hasNext())
return sb.append(']').toString();
sb.append(',').append(' ');
}
}
複製代碼
7.注意擴容方法ensureCapacityInternal()。ArrayList在每次增長元素(多是1個,也多是一組)時,都要調用該方法來確保足夠的容量。當容量不足以容納當前的元素個數時,就設置新的容量爲舊的容量的1.5倍加1,若是設置後的新容量還不夠,則直接新容量設置爲傳入的參數(也就是所需的容量),然後用Arrays.copyof()方法將元素拷貝到新的數組。從中能夠看出,當容量不夠時,每次增長元素,都要將原來的元素拷貝到一個新的數組中,很是之耗時,也所以建議在事先能肯定元素數量的狀況下,才使用ArrayList,不然不建議使用。數組