1、對於ArrayList須要掌握的七點內容java
2、源碼分析算法
2.一、ArrayList的建立(常見的兩種方式)數組
List<String> strList = new ArrayList<String>(); List<String> strList2 = new ArrayList<String>(2);
ArrayList源代碼:安全
基本屬性:網絡
//對象數組:ArrayList的底層數據結構 private transient Object[] elementData; //elementData中已存放的元素的個數,注意:不是elementData的容量 private int size;
注意:數據結構
/** * Save the state of the <tt>ArrayList</tt> instance to a stream (that is, * serialize it). * * @serialData The length of the array backing the <tt>ArrayList</tt> * instance is emitted (int), followed by all of its elements * (each an <tt>Object</tt>) in the proper order. */ 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(); } } /** * Reconstitute the <tt>ArrayList</tt> instance from a stream (that is, * deserialize it). */ private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { // Read in size, and any hidden stuff s.defaultReadObject(); // Read in array length and allocate array int arrayLength = s.readInt(); Object[] a = elementData = new Object[arrayLength]; // Read in all elements in the proper order. for (int i = 0; i < size; i++) a[i] = s.readObject(); }
構造器:ide
/** * 建立一個容量爲initialCapacity的空的(size==0)對象數組 */ public ArrayList(int initialCapacity) { super();//即父類protected AbstractList() {} if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Capacity:" + initialCapacity); this.elementData = new Object[initialCapacity]; } /** * 默認初始化一個容量爲10的對象數組 */ public ArrayList() { this(10);//即上邊的public ArrayList(int initialCapacity){}構造器 }
在咱們執行new ArrayList<String>()時,會調用上邊的無參構造器,創造一個容量爲10的對象數組。源碼分析
在咱們執行new ArrayList<String>(2)時,會調用上邊的public ArrayList(int initialCapacity),創造一個容量爲2的對象數組。this
注意:spa
protected AbstractList() { }
2.二、往ArrayList中添加對象(常見的兩個方法add(E)和addAll(Collection<? extends E> c))
2.2.一、add(E)
strList2.add("hello");
ArrayList源代碼:
/** * 向elementData中添加元素 */ public boolean add(E e) { ensureCapacity(size + 1);//確保對象數組elementData有足夠的容量,能夠將新加入的元素e加進去 elementData[size++] = e;//加入新元素e,size加1 return true; }
/** * 確保數組的容量足夠存放新加入的元素,若不夠,要擴容 */ public void ensureCapacity(int minCapacity) { modCount++; int oldCapacity = elementData.length;//獲取數組大小(即數組的容量) //當數組滿了,又有新元素加入的時候,執行擴容邏輯 if (minCapacity > oldCapacity) { Object oldData[] = elementData; int newCapacity = (oldCapacity * 3) / 2 + 1;//新容量爲舊容量的1.5倍+1 if (newCapacity < minCapacity)//若是擴容後的新容量仍是沒有傳入的所需的最小容量大或等於(主要發生在addAll(Collection<? extends E> c)中) newCapacity = minCapacity;//新容量設爲最小容量 elementData = Arrays.copyOf(elementData, newCapacity);//複製新容量 } }
在上述代碼的擴容結束後,調用了Arrays.copyOf(elementData, newCapacity)方法,這個方法中:對於咱們這裏而言,先建立了一個新的容量爲newCapacity的對象數組,而後使用System.arraycopy()方法將舊的對象數組複製到新的對象數組中去了。
注意:
2.2.二、addAll(Collection<? extends E> c)
使用方式:
List<String> strList = new ArrayList<String>(); strList.add("jigang"); strList.add("nana"); strList.add("nana2"); List<String> strList2 = new ArrayList<String>(2); strList2.addAll(strList);
源代碼:
/** * 將c所有加入elementData */ public boolean addAll(Collection<? extends E> c) { Object[] a = c.toArray();//將c集合轉化爲對象數組a int numNew = a.length;//獲取a對象數組的容量 ensureCapacity(size + numNew);//確保對象數組elementData有足夠的容量,能夠將新加入的a對象數組加進去 System.arraycopy(a, 0, elementData, size, numNew);//將對象數組a拷貝到elementData中去 size += numNew;//從新設置elementData中已加入的元素的個數 return numNew != 0;//若加入的是空集合則返回false }
注意:
public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
將數組src從下標爲srcPos開始拷貝,一直拷貝length個元素到dest數組中,在dest數組中從destPos開始加入先的srcPos數組元素。
除了以上兩種經常使用的add方法外,還有以下兩種:
2.2.三、add(int index, E element)
/** * 在特定位置(只能是已有元素的數組的特定位置)index插入元素E */ public void add(int index, E element) { //檢查index是否在已有的數組中 if (index > size || index < 0) throw new IndexOutOfBoundsException("Index:"+index+",Size:"+size); ensureCapacity(size + 1);//確保對象數組elementData有足夠的容量,能夠將新加入的元素e加進去 System.arraycopy(elementData, index, elementData, index+1, size-index);//將index及其後邊的全部的元素整塊後移,空出index位置 elementData[index] = element;//插入元素 size++;//已有數組元素個數+1 }
注意:
2.2.四、set(int index, E element)
/** * 更換特定位置index上的元素爲element,返回該位置上的舊值 */ public E set(int index, E element) { RangeCheck(index);//檢查索引範圍 E oldValue = (E) elementData[index];//舊值 elementData[index] = element;//該位置替換爲新值 return oldValue;//返回舊值 }
2.三、獲取ArrayList中的單個對象(get(int index))
實現方式:
ArrayList<String> strList2 = new ArrayList<String>(2); strList2.add("hello"); strList2.add("nana"); strList2.add("nana2"); System.out.println(strList2.get(0));
源代碼:
/** * 按照索引查詢對象E */ public E get(int index) { RangeCheck(index);//檢查索引範圍 return (E) elementData[index];//返回元素,並將Object轉型爲E }
/** * 檢查索引index是否超出size-1 */ private void RangeCheck(int index) { if (index >= size) throw new IndexOutOfBoundsException("Index:"+index+",Size:"+size); }
注:這裏對index進行了索引檢查,是爲了將異常內容寫的詳細一些而且將檢查的內容縮小(index<0||index>=size,注意這裏的size是已存儲元素的個數);
事實上不檢查也能夠,由於對於數組而言,若是index不知足要求(index<0||index>=length,注意這裏的length是數組的容量),都會直接拋出數組越界異常,而假設數組的length爲10,當前的size是2,你去計算array[9],這時候得出是null,這也是上邊get爲何減少檢查範圍的緣由。
2.四、刪除ArrayList中的對象
2.4.一、remove(Object o)
使用方式:
strList2.remove("hello");
源代碼:
/** * 從前向後移除第一個出現的元素o */ public boolean remove(Object o) { if (o == null) {//移除對象數組elementData中的第一個null for (int index = 0; index < size; index++) if (elementData[index] == null) { fastRemove(index); return true; } } else {//移除對象數組elementData中的第一個o for (int index = 0; index < size; index++) if (o.equals(elementData[index])) { fastRemove(index); return true; } } return false; } /* * 刪除單個位置的元素,是ArrayList的私有方法 */ 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; //將最後一個元素設爲null,在下次gc的時候就會回收掉了 }
2.4.二、remove(int index)
使用方式:
strList2.remove(0);
源代碼:
/** * 刪除指定索引index下的元素,返回被刪除的元素 */ public E remove(int index) { RangeCheck(index);//檢查索引範圍 E oldValue = (E) elementData[index];//被刪除的元素 fastRemove(index); return oldValue; }
注意:
2.五、判斷對象是否存在於ArrayList中(contains(E))
源代碼:
/** * 判斷動態數組是否包含元素o */ public boolean contains(Object o) { return indexOf(o) >= 0; } /** * 返回第一個出現的元素o的索引位置 */ public int indexOf(Object o) { if (o == null) {//返回第一個null的索引 for (int i = 0; i < size; i++) if (elementData[i] == null) return i; } else {//返回第一個o的索引 for (int i = 0; i < size; i++) if (o.equals(elementData[i])) return i; } return -1;//若不包含,返回-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; }
注意:
2.六、遍歷ArrayList中的對象(iterator())
使用方式:
List<String> strList = new ArrayList<String>(); strList.add("jigang"); strList.add("nana"); strList.add("nana2"); Iterator<String> it = strList.iterator(); while (it.hasNext()) { System.out.println(it.next()); }
源代碼:iterator()方法是在AbstractList中實現的,該方法返回AbstractList的一個內部類Itr對象
public Iterator<E> iterator() { return new Itr();//返回一個內部類對象 }
Itr:
private class Itr implements Iterator<E> { int cursor = 0;//標記位:標記遍歷到哪個元素 int expectedModCount = modCount;//標記位:用於判斷是否在遍歷的過程當中,是否發生了add、remove操做 //檢測對象數組是否還有元素 public boolean hasNext() { return cursor != size();//若是cursor==size,說明已經遍歷完了,上一次遍歷的是最後一個元素 } //獲取元素 public E next() { checkForComodification();//檢測在遍歷的過程當中,是否發生了add、remove操做 try { E next = get(cursor++); return next; } catch (IndexOutOfBoundsException e) {//捕獲get(cursor++)方法的IndexOutOfBoundsException checkForComodification(); throw new NoSuchElementException(); } } //檢測在遍歷的過程當中,是否發生了add、remove等操做 final void checkForComodification() { if (modCount != expectedModCount)//發生了add、remove操做,這個咱們能夠查看add等的源代碼,發現會出現modCount++ throw new ConcurrentModificationException(); } }
遍歷的整個流程結合"使用方式"與"Itr的註釋"來看。注:上述的Itr我去掉了一個此時用不到的方法和屬性。
3、總結
作以上總結,主要是爲了與後邊的LinkedList做比較。
elementData