今天電話面試,面試官問到ArrayList中add方法的原理,我一下蒙了,這個用的最多的一種數據結構被我忽視了,沒答上來,有時候用的最多的反而最自信,最自信的東西最容易忽視其中的細節,因此今天決定扒扒源碼。
1 ArrayList是什麼?
一種用來存放數據的數據結構。
它的底層是一個數組,用來存放數據。
2 ArrayList的特點?
- 底層爲數組。
- 所存儲的數據在內存中連續。
- 查詢的時候以地址偏移量來計算數據位置,因此時間複雜度爲O(1)。//這裏是指用索引作爲參數查詢,而非使用數組相應位置的內容作爲參數查詢。
- 添加數據有兩種方式,添加在數組的最後,或者根據輸入的索引位置添加到數組的相應位置。
- 第一種方式添加數據時,會判斷數組有沒有初始化,沒有初始化時,會把數組初始化爲10大小的容量,並且保證數組容量足夠,不足的情況會調用擴容函數。並且會調用修改次數+1的函數。
- 第二種方式添加數據時,先判斷index參數是否數組下表越界。再執行與第一種方式一樣的操作(上一行的藍色文字),之後再進行插入,插入時,索引位置和之後的元素向後移一位。
- ArrayList有兩種初始化的方式,一種空參默認的初始化,一種指定容量的初始化方式。
- 數組採用默認初始化方式時,默認的容量爲10。
- 擴容時,以1.5倍原數組最大容量的方式擴容。
3.源碼分析(1460行)
源碼比較多的,我另開博客做了解讀,源碼較短較簡單的,我直接說明了源碼的作用。
源碼類型 | 源碼概覽 | 源碼分析 |
---|---|---|
屬性 | private static final long serialVersionUID = 8683452581122892189L; | 序列化時用來驗證數據一致性的值 |
屬性 | private static final int DEFAULT_CAPACITY = 10; | 空參構造初始化數組時,數組的默認大小爲10 |
屬性 | private static final Object[] EMPTY_ELEMENTDATA = {}; | 一個空的數組 |
屬性 | private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; | 共享空數組實例,用於默認大小的空實例。我們將其與DEFAULTCAPACITY_EMPTY_ELEMENTDATA分開來,以瞭解添加第一個元素時要擴容多少。 |
屬性 | transient Object[] elementData; | 一個臨時使用的數組 |
屬性 | private int size; | 當前數組裏所存放元素的個數 |
方法 | public ArrayList(int initialCapacity) {} | 有參的構造方法,指定數組的初始容量大小 |
方法 | public ArrayList() {} | 默認的空參構造。默認的初始化容量大小爲10 |
方法 | public ArrayList(Collection<? extends E> c) {} | 按照入參集合的迭代器返回的元素的順序構造包含指定集合的元素的數組。 |
方法 | public void trimToSize() {} | 把數組的容量變爲當前數組中包含元素的大小,此方法用來最小化數組在存儲空間所佔用的大小。 |
方法 | public void ensureCapacity(int minCapacity) {} | 用來在添加數據時,保證數組容量大於數組所包含的元素個數的。 |
方法 | private void ensureCapacityInternal(int minCapacity) {} | 當數組爲空數組時,最小容量設置爲傳入的參數,並且最後調用函數(下一行) |
方法 | private void ensureExplicitCapacity(int minCapacity) {} |
增加一次修改數組的次數,修改數組後,容量若小於數組元素個數,開始擴容。 |
屬性 | private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; | 能分配的數組最大的大小。某些虛擬機在數組中保留一些頭字。嘗試分配較大的數組可能導致內存不足錯誤:請求的數組大小超過了VM限制,因此數組的最大容量 MAX_ARRAY_SIZE要在Integer.MAX_VALUE上要減去8個!!Integer.MAX_VALUE爲 |
方法 | private void grow(int minCapacity) {} | https://blog.csdn.net/qq_29519041/article/details/86246881 |
方法 | private static int hugeCapacity(int minCapacity) {} | https://blog.csdn.net/qq_29519041/article/details/86246881 |
方法 | public int size() {} | 返回數組中存放的元素個數 |
方法 | public boolean isEmpty() {} | 當數組中沒有元素時 返回true 否者爲false |
方法 | public boolean contains(Object o) {} | 作用:判斷數組中是否包含 o 對象,原理:調用了indexOf做判斷。看下一行 |
方法 | public int indexOf(Object o) {} |
https://blog.csdn.net/qq_29519041/article/details/86259595 就是普通的遍歷查找,並返回下標。 |
方法 | public int lastIndexOf(Object o) {} | 與indexOf方法的唯一區別是,遍歷從尾部開始。其他和indexOf完全一樣。 |
方法 | public Object clone() {} |
淺拷貝!,就是對一個對象只複製他們對應的引用,這些複製後的引用指向的還是之前的對象。就是說,對象的實例只有一個。但是有兩個引用指向他們。我自己的理解。若有錯誤請留言 關於深拷貝淺拷貝相關優秀博文: https://blog.csdn.net/github_38687585/article/details/79926909 |
方法 | public Object[] toArray() {} |
返回包含此列表中所有元素的數組。 (從第一個元素到最後一個元素)。 <p>返回的數組將是“安全的”,因爲此列表不維護對它的引用。(換句話說,此方法必須分配一個新數組)。 因此,調用方可以自由修改返回的數組。 <p>此方法充當基於數組和基於集合的API之間的橋樑。 |
方法 | public <T> T[] toArray(T[] a) {} |
https://rogerfederer.iteye.com/blog/788795
它與上面的方法的區別是,它會以傳入的數組的類型來返回對應類型的數組。 |
方法 | E elementData(int index) {} | 返回數組索引位置的元素,它將會被get(int index)調用 |
方法 | public E get(int index) {} | 返回數組索引位置的元素,首先會調用rangeCheck判斷是否數組索引越界,之後調用elementData(int index)方法。 |
方法 | public E set(int index, E element) {} | 把element替換掉index位置的元素,並返回被替換掉的元素 |
方法 | public boolean add(E e) {} | 在尾部添加元素,之前會對元素修改次數做累加。 |
方法 | public void add(int index, E element) {} | 在數組相應位置添加元素,對應位置及以後的元素向後移一位,這也是數組插入數據慢的原因。需要移動後面的元素。 |
方法 | public E remove(int index) {} | https://blog.csdn.net/qq_29519041/article/details/86316290 |
方法 | public boolean remove(Object o) {} | https://blog.csdn.net/qq_29519041/article/details/86316290 |
方法 | private void fastRemove(int index) {} | |
方法 | public void clear() {} | |
方法 | public boolean addAll(Collection<? extends E> c) {} | |
方法 | public boolean addAll(int index, Collection<? extends E> c) {} | |
方法 | protected void removeRange(int fromIndex, int toIndex) {} | |
方法 | private void rangeCheck(int index) {} | |
方法 | private void rangeCheckForAdd(int index) {} | |
方法 | private String outOfBoundsMsg(int index) {} | |
方法 | public boolean removeAll(Collection<?> c) {} | |
方法 | public boolean retainAll(Collection<?> c) {} | |
方法 | private boolean batchRemove(Collection<?> c, boolean complement) {} | |
方法 | private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException{ |
|
方法 | private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException {} |
|
方法 | public ListIterator<E> listIterator(int index) {} | |
方法 | public ListIterator<E> listIterator() {} | |
方法 | public Iterator<E> iterator() {} | |
方法 | private class Itr implements Iterator<E> {} | |
方法 | public boolean hasNext() {} | |
方法 | public E next() {} | |
方法 | public void remove() {} | |
方法 | public void forEachRemaining(Consumer<? super E> consumer) {} | |
方法 | final void checkForComodification() {} | |
內部類ListItr | private class ListItr extends Itr implements ListIterator<E> {} | |
ListItr的方法 | public boolean hasPrevious() { | |
ListItr的方法 | public int nextIndex() { | |
ListItr的方法 | public int previousIndex() { | |
ListItr的方法 | public E previous() { | |
ListItr的方法 | public void set(E e) { | |
ListItr的方法 | public void add(E e) { | |
方法 | public List<E> subList(int fromIndex, int toIndex) { | |
靜態方法 | static void subListRangeCheck(int fromIndex, int toIndex, int size) { | |
1460行的代碼太多了,慢慢更吧...
------------- 最後結束脩改於2019年1月 10日 22:45 待續... -------------
------------- 最後結束脩改於2019年1月 11日 20:46 待續... -------------