摘要:概述列表是一款即實用又常用的數據結構,用來存儲線性結構的數據。在中對的支持主要有兩種,也是最常用的兩種。本文主要分析的源碼。的底層主要是基于鏈表來實現的。但是返回的卻沒有這樣的等同關系。那么其方法返回的只是一個類型的數組,而不是類型。
概述
列表(list)是一款即實用又常用的數據結構,用來存儲線性結構的數據。在JDK中對List的支持主要有兩種,也是最常用的兩種。一種是ArrayList,一種是LinkedList。
而且這兩種list的區別也經常出現在節操公司的面試題中。節操高一點可能還會問某種list的具體實現,下面說說這兩種List的區別。本文主要分析ArrayList的源碼。
ArrayList
ArrayList的底層主要是基于數組來實現的。
眾所周知,數組是有get√到RandomAccess技能的,也就是說,array[index]這個操作的時間復雜度為O(1)。
但是,在數組上進行增刪操作,就需要比較費力了,往數組中插入一項,需要將其后面的所有項都向后移動一個單元,刪除數組的一項,則需要把后面的所有項都向前移動一位。
最好的情況下,你要插入或者刪除的那一項剛好位于數組的末端,那么就無需再移動任何數據。
所以,如果增刪操作比較多,則不應該選用基于數組實現的ArrayList。
LinkedList
LinkedList的底層主要是基于鏈表來實現的。
鏈表的優勢就是增刪操作不需要像ArrayList一樣拷貝數組(移動意味著拷貝),只需要更改前后節點的引用。菊葛麗子,節點A和節點B中間需要插入一個節點C。一開始,A的后繼是B,B的前驅是A(不明白前驅后繼這兩個術語請查找數據結構方面的書),要插入C時,只需要做以下幾個操作:
將A的后繼改為C
將C的前驅改為A
將C的后繼改為B
將B的前驅改為C。
刪除操作同理可推。
但是,LinkedList要進行隨機查找可就麻煩了,必須得先從鏈表的一端(從頭部或者尾部)開始找,我要查找第10個,它必須先找到第1個,然后第2個,... ,第9個,然后才是第十個。因此,LinkedList適合增刪操作比較多的場景。
可以看看JDK源碼中LinkedList的訪問函數:
Node深拷貝與淺拷貝node(int index) { // assert isElementIndex(index); if (index < (size >> 1)) { Node x = first; for (int i = 0; i < index; i++) x = x.next; return x; } else { Node x = last; for (int i = size - 1; i > index; i--) x = x.prev; return x; } }
翻開ArrayList的源碼,可以看到其中有一個clone()方法,用于克隆當前ArrayList實例。
其方法實現如下:
/** * Returns a shallow copy of this ArrayList instance. (The * elements themselves are not copied.) * * @return a clone of this ArrayList instance */ public Object clone() { try { @SuppressWarnings("unchecked") ArrayListv = (ArrayList ) super.clone(); v.elementData = Arrays.copyOf(elementData, size); v.modCount = 0; return v; } catch (CloneNotSupportedException e) { // this shouldn"t happen, since we are Cloneable throw new InternalError(); } }
可以看到,先調用了super.clone()方法復制了一個全新的對象,并把它賦值給v。由于java.lang.Object.clone()只是一種淺復制,所以,v的elementData引用的還是當前ArrayList的elementData的引用,這就意味著,在v上做操作,會影響原來的ArrayList的值。這也是為什么需要再跟上這么一個語句的原因v.elementData = Arrays.copyOf(elementData, size);。這句話的作用是對原來ArrayList的elementData進行一個數組拷貝,然后賦值給v的elementData,這樣,v的elementData就是原先ArrayList的elementData的一個副本,不再是內存中同一塊數組。在v上的操作也不會影響到原先的ArrayList。這才是ArrayList的clone()方法真正想做的事情。
toArray的坑在ArrayList的構造函數中,可以看到有這樣一個構造函數:
public ArrayList(Collection extends E> c) { elementData = c.toArray(); size = elementData.length; // c.toArray might (incorrectly) not return Object[] (see 6260652) if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); }
這個構造函數用另外一個集合中的元素來構造ArrayList。
但是,注意到其中一句注釋
c.toArray might (incorrectly) not return Object[] (see 6260652)
說c的toArray()方法有可能不返回類型為Object[]的數組。并且,在jdk的bug列表中編號為6260652的bug可以找到解釋:
A DESCRIPTION OF THE PROBLEM :
The Collection documentation claims thatcollection.toArray()is "identical in function" tocollection.toArray(new Object[0]);
However, the implementation of Arrays.asList does not follow this: If created with an array of a subtype (e.g. String[]), its toArray() will return an array of the same type (because it use clone()) instead of an Object[].
If one later tries to store non-Strings (or whatever) in that array, an ArrayStoreException is thrown.
Either the Arrays.asList() implementation (which may return an array of component type not Object) or the Collection toArray documentation (which does not allow argumentless toArray() to return arrays of subtypes of Object) is wrong.
在Java中,數組是不具備有兼容性的,舉個例子:
/** * * * @author Bean * @date 2015年7月15日 下午5:14:35 * @version 1.0 * */ public class Box { public static void main(String[] args) { Object[] obj1 = new Object[1]; obj1[0] = new Student(); obj1[0] = new Person(); Object[] obj2 = new Person[1]; obj2[0] = new Person(); obj2[0] = new Student(); Object[] obj3 = new String[1]; obj3[0] = new Person(); } } class Person { } class Student extends Person { }
在main方法的最后一行將會拋出一個異常
Exception in thread "main" java.lang.ArrayStoreException: cn.com.beansoft.box.Person at cn.com.beansoft.box.Box.main(Box.java:34)
在Collection的API中,聲稱toArray()與toArray(new Object[0])方法是等同的。但是Arrays.asList返回的List卻沒有這樣的等同關系。其實在Arrays類中也有另外一個ArrayList類,只不過它是個內部類,Arrays.asList就是返回的這個內部的ArrayList。源碼如下所示:
public staticList asList(T... a) { return new ArrayList<>(a); }
而這個內部的ArrayList,其toArray方法是調用clone來實現的。如下:
public Object[] toArray() { return a.clone(); }
所以,如果Arrays.asList(T... array)里面的T不是Object類型,而是String類型。那么其toArray方法返回的只是一個String[]類型的數組,而不是Object[]類型。
java.util.ArrayList這個類,本來就是個泛型類。如果它底層的數組類型不是Object[]類型,那么實現泛型存儲就是一件無法實現的事了。
上面所說的Java中數組是類型不兼容的,說白了就是在Java中我們可以這樣做:
Object o = new Person(); o = new String();
但是不能這樣做:
Object[] obj3 = new String[1]; obj3[0] = new Person();
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/64425.html
摘要:源碼解讀屬性默認的初始化空間空的數組用于空對象初始化存儲數組,非私有簡化了嵌套類訪問實際存儲的數據量集合被操作次數,次數對不上拋出構造方法設置初始空間大小的構造方法大于就構造對應長度的數組等于就直接賦值空的數組對象小于就拋出異常無參構造方法 ArrayList源碼解讀 屬性 private static final int DEFAULT_CAPACITY = 10;/...
摘要:前面已經講解集合中的并且也對其中使用的紅黑樹結構做了對應的說明,這次就來看下簡單一些的另一個集合類,也是日常經常使用到的,整體來說,算是比較好理解的集合了,一起來看下前言版本類定義繼承了,實現了,提供對數組隊列的增刪改查操作實現接口,提供隨 前面已經講解集合中的HashMap并且也對其中使用的紅黑樹結構做了對應的說明,這次就來看下簡單一些的另一個集合類,也是日常經常使用到的ArrayL...
摘要:將之前第位置的元素置空,返回被刪除的元素。平常常用的迭代器方法就是判斷當前索引是否等于。最重要的是會更新,此時調用了父類的方法,會使,所以更新了,讓后續的檢查不會拋異常。 本篇主要介紹ArrayList的用法和源碼分析,基于jdk1.8,先從List接口開始。 List List接口定義了如下方法: int size(); boolean isEmpty(); boolean con...
摘要:加載因子是哈希表在其容量自動增加之前可以達到多滿的一種尺度。當哈希表中的條目數超出了加載因子與當前容量的乘積時,則要對該哈希表進行操作即重建內部數據結構,從而哈希表將具有大約兩倍的桶數。成員變量每個對由封裝,存在了對象數組中。 雖是讀書筆記,但是如轉載請注明出處 http://segmentfault.com/blog/exploring/ .. 拒絕伸手復制黨 LinkedLis...
摘要:以下指代數組,指代數組列表。常見的轉換方法是或。在的使用過程中需要注意,當要轉換的長度小于的時,不要試圖通過傳入形參的方式進行轉換,雖然這在的長度大于時不會出現問題。所以,極度建議在轉換之前初始化的長度為的,并且使用返回值重新給賦值。 Array 和 List 都是我們在開發過程中常見的數據結構。我們都知道 Array 是定長的,List 是可變長。而且,List 的實現類 Array...
閱讀 3621·2021-09-30 09:59
閱讀 2229·2021-09-13 10:34
閱讀 577·2019-08-30 12:58
閱讀 1507·2019-08-29 18:42
閱讀 2198·2019-08-26 13:44
閱讀 2922·2019-08-23 18:12
閱讀 3321·2019-08-23 15:10
閱讀 1625·2019-08-23 14:37