點贊在看,養成習慣。java
點贊收藏,人生輝煌。面試
點擊關注【微信搜索公衆號:編程背鍋俠】,防止迷路。編程
List
接口的可調整大小的數組實現。數組
數組:一旦初始化長度就不能夠發生改變 。微信
增刪慢:每次刪除元素,都須要更改數組長度、拷貝以及移動元素位置。數據結構
查詢快:因爲數組在內存中是一塊連續空間,所以能夠根據地址+索引的方式快速獲取對應位置上的元素。工具
private static final int DEFAULT_CAPACITY = 10;
複製代碼
private static final Object[] EMPTY_ELEMENTDATA = {};
複製代碼
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
複製代碼
ArrayList
的底層數據結構,transient
表示該字段不進行序列化操做transient Object[] elementData;
複製代碼
ArrayList
的大小,就是集合中元素的個數private int size;
複製代碼
Constructor | Constructor描述 |
---|---|
ArrayList() | 構造一個初始容量爲十的空列表。 |
ArrayList(int initialCapacity) | 構造具備指定初始容量的空列表。 |
ArrayList(Collection<? extends E> c) | 構造一個包含指定集合的元素的列表,按照它們由集合的迭代器返回的順序。 |
ArrayList()
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
複製代碼
@Test
public void test_get(){
// 運行這行代碼真的會構造一個初始容量爲10的集合嗎?
List<Integer> list = new ArrayList<>();
}
複製代碼
@Test
public void test_get(){
try {
List<String> list = new ArrayList<>();
Field field = list.getClass().getDeclaredField("elementData");
field.setAccessible(true);
int size = ((Object[]) field.get(list)).length;
System.out.println(size);// 0 事實證實並無初始化一個容量爲10的集合
// 添加第一個元素
list.add("1");
Field field2 = list.getClass().getDeclaredField("elementData");
field2.setAccessible(true);
int size2 = ((Object[]) field2.get(list)).length;
System.out.println(size2);// 10 事實證實添加第一個元素的時候才進行初始化容量10
} catch (Exception e) {
e.printStackTrace();
}
}
複製代碼
結論:經過以上的代碼演示證實空參構造方法建立集合對象並未構造一個初始容量爲十的空列表,僅僅將DEFAULTCAPACITY_EMPTY_ELEMENTDATA 的地址賦值給elementData。只有添加第一個元素的時候纔會將容量初始化爲10post
ArrayList(int initialCapacity)
public ArrayList(int initialCapacity) {
// 容量大於0,按照指定的容量初始化數組
if (initialCapacity > 0) {
// 建立一個數組,且指定長度爲initialCapacity
this.elementData = new Object[initialCapacity];
// 若是initialCapacity容量爲0,把EMPTY_ELEMENTDATA的地址賦值給elementData
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
// 容量小於0,拋非法異常
} else {
throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
}
}
複製代碼
@Test
public void test_c(){
// 建立一個帶有初始容量的集合,這個集合建立之後真的初始容量爲5嗎?
List<Integer> list = new ArrayList<>(5);
}
複製代碼
@Test
public void test_get(){
try {
// 構造一個長度爲5的集合
List<String> list = new ArrayList<>(5);
Field field = list.getClass().getDeclaredField("elementData");
field.setAccessible(true);
int size = ((Object[]) field.get(list)).length;
System.out.println(size);// 5 確實是咱們本身指定的容量
} catch (Exception e) {
e.printStackTrace();
}
}
複製代碼
結論:根據
ArrayList
構造方法參數建立指定長度的數組。測試
按照集合迭代器返回的順序,構造一個list包含指定集合的元素。c參數:元素將被放到list中的集合指定的集合爲null,將會拋出NullPointerExceptionthis
ArrayList(Collection<? extends E> c)
public ArrayList(Collection<? extends E> c) {
// 將給定的集合對象轉成數組,且將數組的地址賦值給elementData
elementData = c.toArray();
// 將elementData的長度賦值給集合長度size,且判斷是否不等於 0
if ((size = elementData.length) != 0) {
// 判斷elementData 和 Object[] 是否爲不同的類型
// c.toArray()數組不是object數組進行轉換成Object[]
// 每一個集合的toarray()的實現方法不同,因此須要判斷一下,若是不是Object[].class類型,那麼就須要使用ArrayList中的方法去改造一下。
// elementData.getClass()究竟是什麼類型的?下面搞個例子測試一下
if (elementData.getClass() != Object[].class)
// 轉換Object[] 【在個人其餘文章中的新增源碼裏面有分析】
// 若是不同,使用Arrays的copyOf方法進行元素的拷貝
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// 給定的數組的長度爲0,用空數組代替
this.elementData = EMPTY_ELEMENTDATA;
}
}
複製代碼
toArray()
方法及其實現源碼// Collection<E>接口中轉數組接口
// 返回一個包含此集合中全部元素的數組。這個方法能夠保證給定集合的順序返回數組。此方法充當基於數組的API和基於集合的API之間的橋樑。
Object[] toArray();
// ArrayList<E>中的toArray()方法
public Object[] toArray() {
// 調用數組工具類方法進行拷貝
return Arrays.copyOf(elementData, size);
}
複製代碼
Arrays類中的copyOf方法
源碼// Arrays類中的copyOf方法進行數組的拷貝。original原始的數組,newLength新的容量
public static <T> T[] copyOf(T[] original, int newLength) {
// 再次調用方法進行拷貝
return (T[]) copyOf(original, newLength, original.getClass());
}
// 將原始的數組copy到新的容量的數組中的具體實現
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
@SuppressWarnings("unchecked")
// 用三元運算符進行判斷,無論結果如何都是建立一個新數組
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
// 將數組的內容拷貝到 copy 該數組中,使用System.arraycopy 將須要插入的位置(index)後面的元素通通日後移動一位
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
// 返回拷貝元素成功後的數組
return copy;
}
複製代碼
arraycopy
方法/* * @param src the source array.原始的數組 * @param srcPos starting position in the source array.在原始數組中開始的位置 * @param dest the destination array.目標數組 * @param destPos starting position in the destination data.在目標數組中的起始位置 * @param length the number of array elements to be copied.要copy的元素的個數 */
public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
複製代碼
elementData.getClass()
究竟是什麼類型@Test
public void test_c(){
List<Integer> list = new ArrayList<>();
list.add(1);
// getClass()這個方法返回的是該對象的運行時類。
Class<? extends Object[]> aClass = list.toArray().getClass();
System.out.println(aClass);// class [Ljava.lang.Object;
if (aClass != Object[].class){
System.out.println(false);
}else {
System.out.println(true);// 打印結果:true
}
}
複製代碼
Arrays.copyOf方法
接下來驗證一下@Test
public void test_arrays_copy_of(){
Object[] str = new Object[]{"1", "2"};
// 1表明的是要拷貝元素的個數
Object[] arr_ = Arrays.copyOf(str, 1);
System.out.println(Arrays.toString(arr_));
// [1]
Object[] str0 = new Object[]{"3", "4"};
Object[] arr0_ = Arrays.copyOf(str0, 2);
System.out.println(Arrays.toString(arr0_));
// [3, 4]
String[] str1 = new String[]{"5", "6"};
String[] arr1_ = Arrays.copyOf(str1, 5);
System.out.println(Arrays.toString(arr1_));
//[5, 6, null, null, null] 元素不夠會用null填充
}
複製代碼
第一篇:ArrayList中的構造方法源碼在面試中被問到了...抱歉沒準備好!!!告辭
第二篇:面試官讓我講ArrayList中add、addAll方法的源碼...我下次再來
第三篇:工做兩年還沒看過ArrayList中remove、removeAll、clear方法源碼的都來報道吧
創做不易, 很是歡迎你們的點贊、評論和關注(^_−)☆
你的點贊、評論以及關注是對我最大的支持和鼓勵,而你的支持和鼓勵
我繼續創做高質量博客的動力 !!!