ArrayList特徵和結構
ArrayList 繼承了AbstractList,實現了List。它是一個數組隊列,提供了相關的添加、刪除、修改、遍歷等功能。
ArrayList 實現了RandmoAccess接口,即提供了隨機訪問功能。RandmoAccess是java中用來被List實現,爲List提供快速訪問功能的。在ArrayList中,我們即可以通過元素的序號快速獲取元素對象;這就是快速隨機訪問。稍後,我們會比較List的“快速隨機訪問”和“通過Iterator迭代器訪問”的效率。
ArrayList 實現了Cloneable接口,即覆蓋了函數clone(),能被克隆。
ArrayList 實現java.io.Serializable接口,這意味着ArrayList支持序列化,能通過序列化去傳輸。
和Vector不同,ArrayList中的操作不是線程安全的。所以,建議在單線程中才使用ArrayList,而在多線程中可以選擇Vector或者CopyOnWriteArrayList。
ArrayList源碼分析
/** * The array buffer into which the elements of the ArrayList are stored. * The capacity of the ArrayList is the length of this array buffer. */ private transient Object[] elementData; /** * The size of the ArrayList (the number of elements it contains). * * @serial */ private int size;ArrayList中一切操作都是對elementData數組的操作,而size屬性代表ArrayList中元素的個數。
我們看看ArrayList的基本的增、刪、改、查操作:
public boolean add(E e); public void add(int index, E element); public E set(int index, E element); public E remove(int index); public boolean remove(Object o); public int indexOf(Object o);具體操作我就不寫在上面了,我主要討論一下里面涉及到的問題和原理.在add()操作過程中,會先判斷當前數組容量是否足夠插入數據.
擴充容量方法如下:
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;
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
通過源碼,我們可以看到,List集合的容量擴展是按以前容量的1.5倍增長的。add(index,e)的具體過程是怎麼樣的,它是數組的一種插入數據的操作.set(index,e)是將數組index下標的值設爲e,它的返回值是原來index的舊值.這些基本的知識點,都需要我們通過查看ArrayList的源碼去看的。別人說的再多作用也不大,當我們自己去查看ArrayList的源碼時,就會有很多意想不到的收穫的!數組的複製操作
數組的複製有兩種方法:
(1)使用System.arraycopy()複製
eg:
int a[]={1,2,3,4,5,6,7};
int b[]=new int[5];
System.arraycopy(a,2,b,1,3);
//參數的意義:a爲原數組,2爲a中開始複製的位置,b爲目標數組,1爲b中開始的位置,3爲複製的長度
//所以b中的值爲: 0,3,4,5,0
int a[]=new int[]{1,2,3,4,5,6,7};
int b[]=new int[2];
System.arraycopy(a,3,b,0,Math.min(a.length-3,b.length-0));
System.out.println(Arrays.toString(b));
src - 源數組。srcPos - 源數組中的起始位置。
dest - 目標數組。
destPos - 目標數據中的起始位置。
length - 要複製的數組元素的數量。
(2)使用Arrays.copyOf()或Arrays.copyOfRange()方法
常用API如下:
public static boolean[] copyOf(boolean[] original, int newLength); public static byte[] copyOf(byte[] original, int newLength); public static int[] copyOf(int[] original, int newLength); public static short[] copyOf(short[] original, int newLength); public static long[] copyOf(long[] original, int newLength); public static float[] copyOf(flot[] original, int newLength); public static double[] copyOf(double[] original, int newLength); public static T[] copyOf(T[] original, int newLength); public static T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType);可以看到對基本數據類型都有對應的copyof方法,同時也有引用類型的複製 T[] copyOf,其內部都是調用System.arrayCopy()方法實現的.如果需要數組的複製範圍選擇,可以選擇Arrays.copyOfRange()方法。
我們着重來看看 public static T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType); 方法。T,U肯定是兩個不同的類型,那麼又能實現從U數組的內容複製到T數組,那麼能說明什麼呢?
我們查看源碼:
public static T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
裏面先通過 Array.newInstance(),反射創建一個與newType類型相同的數組,然後調用 system.arraycopy方法,實現數組的複製,那麼能夠從U類型數組複製元素到T類型元素,那麼可以確定的是,T類型一定能兼容U類型,比如說向上轉型,或者 Object o=new Integer(12) 我們通過例子說明: Integer[] intArray = new Integer[] { 1, 2, 3, 4, 5 };
Object obj2[] = copyOf(intArray, 4, Object[].class);
for (Object obj : obj2) {
System.out.println(obj);
}
ArrayList數組應用再探
上面一部分都是數組工具類Arrays中常用的一些功能,其複製功能在ArrayList中得到了廣泛的應用.接下來,我們再來看看ArrayList中的另外兩個高級應用:(1)ArrayList轉換成數組
相應API如下:
public Object[] toArray();
public <t> T[] toArray(T[] a);
</t>
第一個方法沒什麼好說的,我們看第二個方法。傳入T[]a,然後返回 T[]類型數組,這是個什麼鬼!還是看它的源代碼吧:
public <t> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
</t>
分析結論如下:當數組a的長度小於size,則創建新的數組,複製ArrayList中數組的內容並返回新數組;當數組a的長度等於size時,複製ArrayList中數組的內容到數組a中,並返回a;當數組a的長度大於size時,複製ArrayList中數組的內容到數組a中,設置a[size]=null,並返回a 大家有沒有感覺一臉懵逼的感覺啊,爲啥要這樣設計咧!反正我感覺,直接傳入T.class參數蠻好的:
@SuppressWarnings("unchecked")
public <t> T[] toArray(Class<? extends T> cls) {
T newType[] = (T[]) Array.newInstance(cls, size);
System.arraycopy(elementData, 0, newType, 0, size);
return newType;
}
</t>
(2)ArrayList的迭代器操作在AbstractList中通過內部類實現Iterator接口,實現迭代,這裏應用到了迭代器模式,大家可以通過查看源碼來體會這種設計.以及實現ListIterator接口,實現List的前後雙向迭代過程。
自己實現ArrayList
研究ArrayList源碼,我們才能更清楚透徹的去了解ArrayList結構和實現。以前總是聽別人說集合裏面的代碼特別的優秀,很值得我們閱讀和借鑑。總是沒時間,也不敢去嘗試,終於花了一天時間來分析和了解ArrayList,並自己模仿寫出自己的ArrayList,雖然有很多東西還是不知道爲啥這樣做,但是還是收穫滿滿的,所以鼓勵大家多看看源碼,在這裏我也無恥的粘上自己的源碼咧!自定義ArrayList下載
最後歡迎大家和我一起討論學習,一起提高!