Iterablejava
集合的最上級接口, 定義了spliterator,iterator和實現了forEach函數
Collection算法
全部集合的父級接口, 定義了通用函數
AbstractCollection數組
實現了Collection中大量函數, 除了特定的幾個函數iterator和size以外的函數
AbstractList數據結構
繼承了AbstractCollection, 實現了List接口的抽象類, 它實現了List中除size(), get(int location)以外的函數 和AbstractCollection相比, 實現了iterator()接口
RandomAccessdom
提供了隨機訪問(隨機查找)功能, 在ArrayList中經過元素的序號快速獲取元素對象, 這就是快速隨機訪問(隨機查找)
Listide
List有序隊列接口, 提供了一些經過下標訪問元素的函數. List中每個元素都有一個索引, 第一個元素是0, 日後每次+1
ArrayList數據結構是數組 經過必定的算法邏輯動態擴容數組的長度, 能夠理解爲ArrayList底層是一個動態數組 與java原生的數組相比, 它的容量能動態增加 內存須要連續的空間保證 除去尾部位置元素的添加, 刪除操做, 其他都涉及到位置移動 隨機查找效率快(下標搜尋)
ArrayList源碼設計的優雅之道 new ArrayList的時候, 並不會真的建立長度10的對象數組 何時往裏面添加元素了,何時纔會完成數組的建立
private static final long serialVersionUID = 8683452581122892189L; private static final int DEFAULT_CAPACITY = 10; private static final Object[] EMPTY_ELEMENTDATA = {}; private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; transient Object[] elementData; // non-private to simplify nested class access private int size; public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } 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); } }
serialVersionUID函數
用來驗證版本一致性的字段, 反序列化時會效驗
DEFAULT_CAPACITY源碼分析
默認容量, 爲10
EMPTY_ELEMENTDATA測試
空對象數組
DEFAULTCAPACITY_EMPTY_ELEMENTDATAui
默認容量空對象數組
elementData
動態數組的空間大小, 包含空元素
size
動態數組的實際大小, 記錄數組裏有多少元素, 不記錄空元素
ArrayList()
無參構造 public ArrayList() {this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;} 把默認容量空對象數組賦值給elementData, 仍是空 無參構造建立動態數組時, size爲0, elementData爲0
ArrayList(int initialCapacity)
帶參構造 參數大於0時 this.elementData = new Object[initialCapacity]; 建立一個大小爲initialCapacity的Object數組賦給elementData 參數小與於0時 this.elementData = EMPTY_ELEMENTDATA; 開一個長度爲10的空間 參數小與於0時 拋異常
代碼測試
public static void main(String[] args) throws Exception { ArrayList arr = new ArrayList(); Field field = arr.getClass().getDeclaredField("elementData"); field.setAccessible(true); Object[] o = (Object[]) field.get(arr); System.out.println(arr.size()); System.out.println(o.length); arr.add("1"); field = arr.getClass().getDeclaredField("elementData"); field.setAccessible(true); o= (Object[]) field.get(arr); System.out.println(arr.size()); System.out.println(o.length); for (int i = 0; i < 16; i++) { arr.add(i); } field = arr.getClass().getDeclaredField("elementData"); field.setAccessible(true); o= (Object[]) field.get(arr); System.out.println(arr.size()); System.out.println(o.length); }
輸出
0 0 1 10 17 22
初始化總結
ArrayList arr = new ArrayList(); elementData爲0 size爲0 ArrayList arr = new ArrayList(1); elementData爲0 size爲1, 走了帶參構造, 參數爲幾, 空間爲幾, ArrayList(int initialCapacity) add("test");//添加元素源碼分析會講爲何空間爲10 elementData爲10 size爲1 若是第一次添加元素數量爲11, 基於動態擴容1.5倍, 它的長度就是15
public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; } private void ensureCapacityInternal(int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); } private static int calculateCapacity(Object[] elementData, int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; } private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); }
添加一個元素1-10個元素時
add(E e)方法 //以無參初始化(ArrayList arr = new ArrayList();), 並調用add(E e)添加一個元素爲例 public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; } 進ensureCapacityInternal(size + 1);此時傳參爲1 (0+1=1) private void ensureCapacityInternal(int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); } 進calculateCapacity, 判斷elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA是否成立 public ArrayList() {this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } 條件成立, 由於無參初始化方法已經賦過值 進if, 判斷10和參數哪一個大, 10大, 返回10 private static int calculateCapacity(Object[] elementData, int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; } 此時返回ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); 這裏, 參數是10 10-10不大於0, 沒走到動態擴容 private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); }
目前是elementData(空間長度爲10), size爲1(size不記錄空元素)
添加元素超出10的狀況, 動態擴容
添加第11個元素時 public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; } 此時minCapacity爲11 private void ensureCapacityInternal(int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); } 不走if, 返回11, 則ensureExplicitCapacity(11) private static int calculateCapacity(Object[] elementData, int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; } 11-10大於0, 進入動態擴容 private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); } 進grow(minCapacity); 動態擴容方法 private void grow(int minCapacity) { 老空間爲10 int oldCapacity = elementData.length; 新空間=10+(10>>1), 位運算10的二進制位爲1010, 右移動1位二進制爲101, 10進製爲5, 因此新空間爲15=10+5 int newCapacity = oldCapacity + (oldCapacity >> 1); 若是新空間-11小於0,則新空間爲11 if (newCapacity - minCapacity < 0) newCapacity = minCapacity; 若是新空間大於0, 則賦值 if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); //下面講這個方法 拷貝一個新的數組和長度的集合,賦值給咱們的對象數組 elementData = Arrays.copyOf(elementData, newCapacity); } MAX_ARRAY_SIZE爲Integer.MAX_VALUE-8 (int最大值-8) 若是要開的空間大於MAX_ARRAY_SIZE, 就把int最大值-8賦值給它 若是不大於MAX_ARRAY_SIZE,則把MAX_ARRAY_SIZE賦值給它 private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; } 官方註釋對於-8的介紹 這8bit裏存了要分配最大數組的大小(數組下標???) 虛擬機中在數據中保留的標題字 (既數組形狀判斷是否爲數組,對象是否同步,數組大小, 不知道是否包含了對象類型的類信息指針???) 嘗試分配更大的數組會拋異常, 其實大小仍是支持到了Integer.MAX_VALUE /** * 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;
以前介紹源碼的時候把modCount++跳過了, 接下來解釋下, 在ArrayList源碼裏有個監聽
源碼
private class Itr implements Iterator<E> { int cursor; // index of next element to return int lastRet = -1; // index of last element returned; -1 if no such int expectedModCount = modCount; Itr() {} 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.this.remove(lastRet); cursor = lastRet; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } @Override @SuppressWarnings("unchecked") public void forEachRemaining(Consumer<? super E> consumer) { Objects.requireNonNull(consumer); final int size = ArrayList.this.size; int i = cursor; if (i >= size) { return; } final Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) { throw new ConcurrentModificationException(); } while (i != size && modCount == expectedModCount) { consumer.accept((E) elementData[i++]); } // update once at end of iteration to reduce heap write traffic cursor = i; lastRet = i - 1; checkForComodification(); } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }
在每次作集合元素操做時,都會改變modCount,在迭代的時候,會把modCount賦值給expectedModCount, 若是在迭代過程當中刪除元素,就會修改modCount,可是迭代器在過程當中不會同步expectedModCount, 每次迭代會會比較是否相等 就是說在使用迭代器的時候, 不能改變元素, 可是能夠使用迭代器的方法去改變
public E get(int index) { rangeCheck(index); return elementData(index); } private void rangeCheck(int index) { if (index >= size) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); } E elementData(int index) { return (E) elementData[index]; }
判斷下標是否越界 複雜度O(1), 直接根據下標位置返回元素
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); elementData[--size] = null; // clear to let GC do its work return oldValue; } public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length); src - 源數組。 srcPos - 源數組中的起始位置。 dest - 目標數組。 destPos - 目的地數據中的起始位置。 length - 要複製的數組元素的數量。
將指定源數組中的數組從指定位置複製到目標數組的指定位置
好比咱們有個數組 值爲 a b c d e f g h i g k 下標 0 1 2 3 4 5 6 7 8 9 10 若是刪除f rangeCheck(index); //效驗下標越界, index=5 int numMoved = size - index - 1; //numMoved= 4 調用System.arraycopy(elementData, index+1, elementData, index, numMoved);
從原數組下標從6開始copy到新數組從下標從5開始的後面