3.2.7.1 請用ArrayList實現Stack以及Queue的功能。
public class ArrayListStack extends ArrayList implements Stack {
ArrayList arrayList =new ArrayList<>() ;
public void push(T obj) {
arrayList.add(obj);
}
public T pop() {
return arrayList.remove(arrayList.size()-1);
}
public int size(){
return arrayList.size();
}
}
3.2.7.2 如果讓你實現Java的ArrayList,你需要考慮哪些要素?
ArrayList是基於數組實現的,是一個動態數組,其容量能自動增長,類似於C語言中的動態申請內存,動態增長內存。
ArrayList實現了Serializable接口,因此它支持序列化,能夠通過序列化傳輸,實現了RandomAccess接口,支持快速隨機訪問,實際上就是通過下標序號進行快速訪問,實現了Cloneable接口,能被克隆。
每個ArrayList實例都有一個容量,該容量是指用來存儲列表元素的數組的大小。它總是至少等於列表的大小。隨着向ArrayList中不斷添加元素,其容量也自動增長。自動增長會帶來數據向新數組的重新拷貝,因此,如果可預知數據量的多少,可在構造ArrayList時指定其容量。在添加大量元素前,應用程序也可以使用ensureCapacity操作來增加ArrayList實例的容量,這可以減少遞增式再分配的數量。
注意,此實現不是同步的。如果多個線程同時訪問一個ArrayList實例,而其中至少一個線程從結構上修改了列表,那么它必須保持外部同步。
ArrayList提供了三種方式的構造器,可以構造一個默認初始容量為10的空列表、構造一個指定初始容量的空列表以及構造一個包含指定collection的元素的列表,這些元素按照該collection的迭代器返回它們的順序排列的。
ArrayList提供了set(int index, E element)、add(E e)、add(int index, E element)、addAll(Collection<? extends E> c)、addAll(int index, Collection<? extends E> c)這些添加元素的方法。
每當向數組中添加元素時,都要去檢查添加后元素的個數是否會超出當前數組的長度,如果超出,數組將會進行擴容,以滿足添加數據的需求。數組擴容通過一個公開的方法ensureCapacity(int minCapacity)來實現。在實際添加大量元素前,我也可以使用ensureCapacity來手動增加ArrayList實例的容量,以減少遞增式再分配的數量。
數組進行擴容時,會將老數組中的元素重新拷貝一份到新的數組中,每次數組容量的增長大約是其原容量的1.5倍。這種操作的代價是很高的,因此在實際使用時,我們應該盡量避免數組容量的擴張。當我們可預知要保存的元素的多少時,要在構造ArrayList實例時,就指定其容量,以避免數組擴容的發生。或者根據實際需求,通過調用ensureCapacity方法來手動增加ArrayList實例的容量。
Fail-Fast機制:
ArrayList也采用了快速失敗的機制,通過記錄modCount參數來實現。在面對並發的修改時,迭代器很快就會完全失敗,而不是冒着在將來某個不確定時間發生任意不確定行為的風險。
3.2.7.3 請通過Iterator對象訪問LinkedList對象,並說明這種訪問方式的好處。
LinkedList list = new LinkedList();
//省略賦值
ListIterator it = list.listIterator();
while(it.hasNext()) {
System.out.println(it.next().toString()); //使用
}
好處是,能用統一的方式來訪問集合對象,這也是迭代器模式的好處。
3.2.7.4 你有沒有讀過ArrayList部分的底層實現源代碼?如果有,請說明下其中的add方法是如何實現的?尤其請考慮動態擴展的情況。
如下是擴容的底層方法
/**
* 增加ArrayList容量。
*
* @param minCapacity 想要的最小容量
*/
public void ensureCapacity(int minCapacity) {
// 如果elementData等於DEFAULTCAPACITY_EMPTY_ELEMENTDATA,最小擴容量為DEFAULT_CAPACITY,否則為0
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)? 0: DEFAULT_CAPACITY;
//如果想要的最小容量大於最小擴容量,則使用想要的最小容量。
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
/**
* 數組容量檢查,不夠時則進行擴容,只供類內部使用。
*
* @param minCapacity 想要的最小容量
*/
private void ensureCapacityInternal(int minCapacity) {
// 若elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA,則取minCapacity為DEFAULT_CAPACITY和參數minCapacity 之間的最大值
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
/**
* 數組容量檢查,不夠時則進行擴容,只供類內部使用
*
* @param minCapacity 想要的最小容量
*/
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// 確保指定的最小容量 > 數組緩沖區當前的長度
if (minCapacity - elementData.length > 0)
//擴容
grow(minCapacity);
}
/**
* 分派給arrays的最大容量
* 為什么要減去8呢?
* 因為某些VM會在數組中保留一些頭字,嘗試分配這個最大存儲容量,可能會導致array容量大於VM的limit,最終導致OutOfMemoryError。
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
* 擴容,保證ArrayList至少能存儲minCapacity個元素
* 第一次擴容,邏輯為newCapacity = oldCapacity + (oldCapacity >> 1);即在原有的容量基礎上增加一半。第一次擴容后,如果容量還是小於minCapacity,就將容量擴充為minCapacity。
*
* @param minCapacity 想要的最小容量
*/
private void grow(int minCapacity) {
// 獲取當前數組的容量
int oldCapacity = elementData.length;
// 擴容。新的容量=當前容量+當前容量/2.即將當前容量增加一半。
int newCapacity = oldCapacity + (oldCapacity >> 1);
//如果擴容后的容量還是小於想要的最小容量
if (newCapacity - minCapacity < 0)
//將擴容后的容量再次擴容為想要的最小容量
newCapacity = minCapacity;
//如果擴容后的容量大於臨界值,則進行大容量分配
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData,newCapacity);
}
/**
* 進行大容量分配
*/
private static int hugeCapacity(int minCapacity) {
//如果minCapacity<0,拋出異常
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
//如果想要的容量大於MAX_ARRAY_SIZE,則分配Integer.MAX_VALUE,否則分配MAX_ARRAY_SIZE
return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;
}
3.2.7.5 請說下Collection和Collections的差別以及各自的用途。
Collections 是一個集合的一個類,其中包含有一些和集合操作相關的靜態多態方法。Jave集合里則有另外一個和它非常相似的接口
Collection(不帶s),它是線性表類集合的父接口,List和Set等接口都是通過實現這個接口來實現的。
3.2.7.6 我們知道Set對象里不能有重復的元素,請說下是用什么方法來判斷是否重復?是通過equals方法嗎?
請參與本書3.2.3 Set集合是如何判斷重復,里面有詳細的描述