ArrayList源碼解析
-
ArrayList 是基于數組的方式來實現數據的增加、刪除、修改、查找
1、內部維護了兩個變量private transient Object[] elementData ;該數組緩存的集合中的元素,集合的容量就是該數組的長度, elementData 是 transient修飾,說明在序列化時 數組 elementData不在序列化范圍內。 private int size; 集合的大小,集合中元素的數量
2、 在看看 ArrayList的構造器
構造一個指定容量的 initialCapacity 的空的集合 super調用的 AbstractList的默認構造方法, 該方法是一個空的方法 然后判斷傳入的參數不能小于 0否則就跑出非法參數異常, 然后直接創建了一個 長度為 initialCapacity的數組對象,并將該對象賦值給當前實例的 elementData屬性,用以存放集合中元素 public ArrayList(int initialCapacity){ super(); if(initialCapacity<0){ throw new IllegalArgumentException("Illegal Capacity initialCapacity") } this.elementData=new Object[initialCapacity]; } public ArrayList(){ this{10}} 構造一個默認為 10的空的集合 再來看一個包含指定集合 中的所有元素的集合,該集合中元素的順序是以 的迭代器返回元素的順序 public ArrayList(Collection<? extend E> c) { elementData=c.toArray(); size=elementData.length; if(elementData.getClass!=Object[].class) { elementData=Arrays.copyof(elementData,size,Object[].class); } }
3、從上面的源碼中 看到是先將 c.toArray()方法返回賦值給 elementData,將大小賦值給 size,然后進行判斷 如果為 true,調用 Arras.copy方法創建一個新的 Object[]新數組,將 elementData 中元素 copy 到新 Object
數組中 然后賦值給 elementData()4、add()方法
插入對象 首先將 size+1,纏上一個 minCapacity的變量,調用 ensureCapacity( minCapacity) 方法保證數組插入一個元素時有可用的容量,然后將元素 放到 elementData的 size位置,size+1 public boolean add(E e){ ensureCapacity(size+1); elementData[size++]=e; return true; } public void ensureCapacity(int minCapacity){ modCount++; int oldCapacity=elementData.length; if(minCapacity>oldCapacity){ Object[] oldData=elementData; int newCapacity=(oldCapacity*3)/2+1; if(newCapacity<minCapacity) newCapacity=minCapacity; elementData=Arrays.copy(elementData,newCapacity); } } 必要時數組的擴容,首先將 modCoun+1 modeCount 表示修改數組結構次數,如果傳入的 minCapacity大于目前 elementData的容量則將容量擴展到 (oldCapacity*3)/2+1若此時容量仍然小于 minCapacity,直接將 minCapacity設置為最新的容量, 最后使用 Arrays的 copy方法到新的數組中 并將新數組賦值給 elementData. 在指定下表 index處插入 element首先判斷下標 index 的參數是否有效,否則跑出異常,然后調用 ensureCapacity(size+1)要確保有足夠容量來插入數據,然后調用 System.arraycopy 從 index 開始,將 elementData 數組元素 copy到從 index+1 開始的地方,copy 的長度為 size-index,這樣 index下標出的位置就空出來了,然后將元素 element 放到下標為 index處的位置,最后將 size++ public void add(int index,E element){ if(index>size || index<0){ throw new IndexOutOfBoundsException("Index"+index+",Size:"+size); } ensureCapacity(size+1); 這一步是擴容 Systim.arraycopy(elementData,index,elementData,index+1,size-index); elementData[index]=element; size++; }
5、刪除方法
刪除指定元素,分為兩種情況, 若指定的元素為 null,則遍歷當前的 elementData 數組,如果某一個下標 index的值為 null,則調用方法 fastRemove 刪除元素, 如果指定的不為 null,則遍歷 若某一個 index 下標處的元素和 o 進行 equals則快速刪除元素 但是只能刪除第一個出現的元素 快速刪除是 將 index后面的元素往前復制一位 ,這樣就達到刪除的目的
6、查找方法
查找非常簡單,(這里不粘代碼了 在下面和LinkedList對比起來看)
-
注意
ArrayList是基于數組的方式實現的, 所以查找效率高,但是每次插入或者刪除元素,都要大 量的移動元素,所以效率很低, 這里面主要涉及幾個重要的方法 一個是減小數組的容量 就是講當前數組的容量縮小為實際存儲的容量trimToSize,還有一個就是擴容,如果容量不夠,就設置新的容量為舊的容量的 1.5倍加1,因此建議能夠事先確定元素數量的情況下用ArrayList(數組都是這樣的特征)
LinkedList源碼解析
-
LinkedList** 繼承結構開始分析
public class LinkedList<E> extends AbstractSequentialList<E>implements List<E>, Deque<E>, Cloneable, java.io.Serializable
LinkedList是 一個繼承于 AbstractSequentialList的雙向鏈表。它也可以被當作堆棧、隊列或雙端隊列
列進 行操作。
LinkedList實現 List接 口,能對它進行隊列操作。
LinkedList實現 Deque接 口,雙向隊列它 支持從兩個端點 方向檢索和插 入元素。
LinkedList實現了了Cloneable接 口,即覆蓋了了函數 clone(),能克隆
LinkedList實現 java.io.Serializable 接 口,這意味著 LinkedList支持序列化,能通過序列化去傳輸。 -
LinkedList屬性
關鍵字 transient 標識的字段的 生命周期僅存于調 用者的內存中 而不不會寫到磁盤 里里持久化transient int size = 0; /** *Pointer to first node. *Invariant: (first == null && last == null) || * (first.prev == null && first.item != null) */ transient Node<E> first; transient Node<E> last; LinkedList每個存儲元素是 一個 Node對象 private static class Node<E> { Node<E> next; Node<E> prev; Node(Node<E> prev, E element, Node<E> next) { this.item = element; this.next = next; this.prev = prev; } } 在這個對象可以分析出LinkedList存儲開銷比 Arraylist大因為 LinkedList每個元素在內存里里多分配了了Node next; , Node prev;兩個變量值得出ArrayList 和 LinkedList 存儲相同 大 小的數據LinkedList占內存 比較 大
-
LinkedList 構造方法
public LinkedList(Collection<? extends E> c) { this(); addAll(c); } return addAll(size, c);開始分析.參數 1.添加位置 , 參數 2,需要添加的數據; public boolean addAll(int index, Collection<? extends E> c) { checkPositionIndex(index); Object[] a = c.toArray(); int numNew = a.length; if (numNew == 0) return false; 上部分校驗數據是否為0但是少了了空指針校驗 Node<E> pred, succ; if (index == size) { 保存index處的節點。插 入位置如果是size,則在頭結點前 面插 入,否則在獲取 index處的節點插 入succ = null; pred = last; } else { 獲取前 一個節點,插 入時需要修改這個節點的next引 用 succ = node(index); pred = succ.prev; } 可以理理解node擺放的位置 for (Object o : a) { 在強制類型轉換的時候編譯器 會給出警告@SuppressWarnings("unchecked")此注解去除警告 @SuppressWarnings("unchecked") E e = (E) o; 創建node參數1.node頭參數2.數據參數3.node尾 Node<E> newNode = new Node<>(pred, e, null); if (pred == null) first = newNode; else 修改前 一節點的next指針 pred.next = newNode; pred = newNode; } 收尾的處理理 if (succ == null) { last = pred; } else { pred.next = succ; succ.prev = pred; } size += numNew; modCount++; return true; }
LinkedList初始化操作 會處理理一 大坨的頭尾地址轉換ArrayList初始化操作 會不不斷的 copy數組為了了
Arrays.copyOf(elementData, size, Object[].class);
(說明 這些東西原來在world上寫的 復制過來格式全部變了 所以我就截圖了)
-
LinkedList 添加元素
image.pnglinkLast方法
image.pnglinkBefore方法(按指定位置插)
image.pngLinkedList 添加 又是 處理理 頭--尾.地址轉換
ArrayList 添加 又會 copy 數組.為了了 自增
image.png -
LinkedList 刪除元素
image.pngunlink方法
刪除節點 把節點的.所有屬性指 null
image.pngLinkedList 刪除元素.先把 自 己的東 西給別 人.然后 自 己指空.
ArrayList 刪除元素.還是操作 Copy 數組image.png -
LinkedList 獲取元素
image.pngnode方法
image.pngLinkedList 獲取元素.需要 用循環找
ArrayList 獲取元素 直接 index 下標查找image.png 上面的源碼分析是對上一篇的線性表的一個擴展,因為ArrayList和LinkedList就是用的順序存儲結構和鏈式存儲結構的,好了 先到這里 大家有什么疑問或者哪里寫的不對 可以指正。