3.1 接口
常見接口
-
Map 接口和 Collection 接口是所有集合框架的父接口;
-
Collection 接口的子接口包括:Set 接口、List 接口和Queue接口;
-
Map 接口的實現類主要有:HashMap、TreeMap、LinkedHashMap、Hashtable、ConcurrentHashMap 以及 Properties 等;
-
Set 接口的實現類主要有:HashSet、TreeSet、LinkedHashSet 等;
-
List 接口的實現類主要有:ArrayList、LinkedList、Stack 、Vector以及CopyOnWriteArrayList 等;
-
Queue接口的主要實現類有:ArrayDeque、ArrayBlockingQueue、LinkedBlockingQueue、PriorityQueue等;
List接口和Set接口的區別
-
List 元素是有序的,可以重複;Set 元素是無序的,不可以重複。
隊列、Set、Map 區別
-
List 有序列表
-
Set無序集合
-
Map鍵值對的集合
-
Queue隊列FlFO
ArrayList
-
基於數組實現,無容量的限制。
-
在執行插入元素時可能要擴容,在刪除元素時並不會減小數組的容量,在查找元素時要遍歷數組,對於非null的元素採取equals的方式尋找。
-
是非線程安全的。
-
注意點:
-
(1)ArrayList隨機元素時間複雜度O(1),插入刪除操作需大量移動元素,效率較低
-
(2)爲了節約內存,當新建容器爲空時,會共享Object[] EMPTY_ELEMENTDATA = {}和 Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}空數組
-
(3)容器底層採用數組存儲,每次擴容爲1.5倍
-
(4)ArrayList的實現中大量地調用了Arrays.copyof()和System.arraycopy()方法,其實Arrays.copyof()內部也是調用System.arraycopy()。System.arraycopy()爲Native方法
-
(5)兩個ToArray方法
-
-
Object[] toArray()方法。該方法有可能會拋出java.lang.ClassCastException異常
-
<T> T[] toArray(T[] a)方法。該方法可以直接將ArrayList轉換得到的Array進行整體向下轉型
-
(6)ArrayList可以存儲null值
-
(7)ArrayList每次修改(增加、刪除)容器時,都是修改自身的modCount;在生成迭代器時,迭代器會保存該modCount值,迭代器每次獲取元素時,會比較自身的modCount與ArrayList的modCount是否相等,來判斷容器是否已經被修改,如果被修改了則拋出異常(fast-fail機制)。
-
成員變量
/**
* 序列號
*/
private static final long serialVersionUID = 8683452581122892189L;
/**
* 默認容量
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* 一個空數組
* 當用戶指定該 ArrayList 容量爲 0 時,返回該空數組
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* 一個空數組實例
* - 當用戶沒有指定 ArrayList 的容量時(即調用無參構造函數),返回的是該數組==>剛創建一個 ArrayList 時,其內數據量爲 0。
* - 當用戶第一次添加元素時,該數組將會擴容,變成默認容量爲 10(DEFAULT_CAPACITY) 的一個數組===>通過 ensureCapacityInternal() 實現
* 它與 EMPTY_ELEMENTDATA 的區別就是:該數組是默認返回的,而後者是在用戶指定容量爲 0 時返回
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* ArrayList基於數組實現,用該數組保存數據, ArrayList 的容量就是該數組的長度
* - 該值爲 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 時,當第一次添加元素進入 ArrayList 中時,數組將擴容值 DEFAULT_CAPACITY(10)
*/
transient Object[] elementData; // non-private to simplify nested class access
/**
* ArrayList實際存儲的數據數量
*/
private int size;
構造方法
/**
* 創建一個初試容量的、空的ArrayList
* @param initialCapacity 初始容量
* @throws IllegalArgumentException 當初試容量值非法(小於0)時拋出
*/
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
添加 add(e)
/**
*增加指定的元素到ArrayList的最後位置
* @param e 要添加的元素
* @return
*/
public boolean add(E e) {
// 確定ArrayList的容量大小---嚴謹
// 注意:size + 1,保證資源空間不被浪費,
// ☆☆☆按當前情況,保證要存多少個元素,就只分配多少空間資源
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
即使初始化時指定大小:小於10個,添加元素時會調整大小,保證capacity不會少於10個。
/**
* 私有方法:明確 ArrayList 的容量,提供給本類使用的方法
* - 用於內部優化,保證空間資源不被浪費:尤其在 add() 方法添加時起效
* @param minCapacity 指定的最小容量
*/
private void ensureCapacityInternal(int minCapacity) {
// 若 elementData == {},則取 minCapacity 爲 默認容量和參數 minCapacity 之間的最大值
// 注:ensureCapacity() 是提供給用戶使用的方法,在 ArrayList 的實現中並沒有使用
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
/**
* - 當用戶沒有指定 ArrayList 的容量時(即調用無參構造函數),返回的是該數組
DEFAULTCAPACITY_EMPTY_ELEMENTDATA
==>剛創建一個ArrayList 時,其內數據量爲 0。
* - 當用戶第一次添加元素時,該數組將會擴容,變成默認容量爲 10(DEFAULT_CAPACITY) 的一個數組
===>通過 ensureCapacityInternal() 實現
* DEFAULTCAPACITY_EMPTY_ELEMENTDATA與 EMPTY_ELEMENTDATA 的區別就是:該數組是默認返回
的,而後者是在用戶指定容量爲 0 時返回
*/
minCapacity= Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
/**
* 私有方法:明確 ArrayList 的容量
* - 用於內部優化,保證空間資源不被浪費:尤其在 add() 方法添加時起效
* @param minCapacity 指定的最小容量
*/
private void ensureExplicitCapacity(int minCapacity) {
// 將“修改統計數”+1,該變量主要是用來實現fail-fast機制的
modCount++;
// 防止溢出代碼:確保指定的最小容量 > 數組緩衝區當前的長度
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
擴容
/**
* 私有方法:擴容,以確保 ArrayList 至少能存儲 minCapacity 個元素
* - 擴容計算:newCapacity = oldCapacity + (oldCapacity >> 1); 擴充當前容量的1.5倍
* @param minCapacity 指定的最小容量
*/
private void grow(int minCapacity) {
// 防止溢出代碼
int oldCapacity = elementData.length;
// 運算符 >> 是帶符號右移. 如 oldCapacity = 10,則 newCapacity = 10 + (10 >> 1) = 10 + 5 = 15
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0) // 若 newCapacity 依舊小於 minCapacity
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0) // 若 newCapacity 大於最大存儲容量,則進行大容量分配
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
Arrays.copyOf底層是System.arrayCopy
添加 add(index,e)
/**
*
*在這個ArrayList中的指定位置插入指定的元素,
* - 在指定位置插入新元素,原先在 index 位置的值往後移動一位
* @param index 指定位置
* @param element 指定元素
* @throws IndexOutOfBoundsException
*/
public void add(int index, E element) {
rangeCheckForAdd(index);//判斷角標是否越界
//看上面的,size+1,保證資源空間不浪費,按當前情況,保證要存多少元素,就只分配多少空間資源
ensureCapacityInternal(size + 1); // Increments modCount!!
//第一個是要複製的數組,第二個是從要複製的數組的第幾個開始,
// 第三個是複製到那,四個是複製到的數組第幾個開始,最後一個是複製長度
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
/**
* 添加時檢查索引是否越界
*/
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
刪除 remove(o)
/**
* 移除list中指定的第一個元素(符合條件索引最低的)
* 如果list中不包含這個元素,這個list不會改變
* 如果包含這個元素,index 之後的所有元素依次左移一位
* @param o 這個list中要被移除的元素
* @return
*/
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
/**
* 快速刪除第 index 個元素
* 和public E remove(int index)相比
* 私有方法,跳過檢查,不返回被刪除的值
* @param index 要刪除的腳標
*/
private void fastRemove(int index) {
modCount++;//這個地方改變了modCount的值了
int numMoved = size - index - 1;//移動的個數
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; //將最後一個元素清除
}
public static void arraycopy(Object src, int srcPos, Object dest, int destPos,int length)
其中:src表示源數組,srcPos表示源數組要複製的起始位置,desc表示目標數組,length表示要複製的長度。
刪除 remove(index)
/**
* 移除指定位置的元素
* index 之後的所有元素依次左移一位
* @param index 指定位置
* @return 被移除的元素
* @throws IndexOutOfBoundsException
*/
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;//要移動的長度
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
// 將最後一個元素置空
elementData[--size] = null;
return oldValue;
}