【Java入門提高篇】Day21 Java容器類詳解(四)ArrayL
今天要介紹的是List介面中最常用的實現類——ArrayList,本篇的原始碼分析基於JDK8,如果有不一致的地方,可先切換到JDK8後再進行操作。
本篇的內容主要包括這幾塊:
1.原始碼結構介紹
2.原始碼展示
3.要點說明
4.優缺點說明
一、原始碼結構介紹ArrayList的原始碼跟之前的介面原始碼比起來,那可就不能同日而語了,一千多行程式碼,如果直接看的話確實有些費勁,但仔細看看就會發現,其實大致結構是這樣的:
其中包含了好四個內部類:
ArrayListSpliterator:ArrayList可分割的迭代器,基於二分法的可分割迭代器,是為了並行遍歷元素而設計的一種迭代器,jdk1.8 中的集合框架中的資料結構都預設實現了 spliterator。
Itr:實現Iterator介面的迭代器,為ArrayList進行最佳化。
ListItr:實現ListIterator介面的迭代器,為ArrayList進行最佳化。
SubList:實現了AbstractList和RandomAccess介面的子列表。
這四個內部類就佔了將近一半的篇幅,足可見其重要性。這四個類中前三個都是跟迭代器有關,最後一個是為了處理區域性列表而設計的子列表類。
二、原始碼展示:
下面是蹩腳翻譯講解版的原始碼:
/**
* ArrayList 是List介面的動態陣列實現,實現了List的所有可選操作,並且允許所有元素,包括null。
* ArrayList 跟Vector差不多,但它不是執行緒安全的。
* ArrayList 的容量會根據列表大小自動調整。在新增大量元素之前,可以使用ensureCapacity 方法來保證列表有足夠空間存放元素。
* ArrayList 不是執行緒安全的,所以如果多條執行緒將要對其進行結構性改變時(如新增刪除元素),需要使用synchronized 進行同步。
* 如果不存在這樣的物件,則需要使用其同步包裝類 Collections.synchronizedList
* List list = Collections.synchronizedList(new ArrayList(...));
*
* iterator() 方法將會返回一個listIterator,其中的方法是“fail-fast(快速失敗的)”,如果在建立了迭代器之後,在用迭代器遍歷一個列表時,
* 如果遍歷過程中對集合物件的內容進行了修改(增加、刪除、修改),則會丟擲Concurrent Modification Exception。
* 但fail-fast 的行為是無法得到保證的,不可能對是否出現不同步併發修改做出任何硬性保證。
* 快速失敗迭代器會盡最大努力丟擲 ConcurrentModificationException。
* 因此,為提高這類迭代器的正確性而編寫一個依賴於此異常的程式是錯誤的做法:迭代器的快速失敗行為應該僅用於檢測 bug。
*/
public class ArrayList extends AbstractList
implements List, RandomAccess, Cloneable, java.io.Serializable
{
private static final long serialVersionUID = 8683452581122892189L;
/**
* 預設容量
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* 空例項共享的空陣列
* todo 為什麼要區分 EMPTY_ELEMENTDATA 與 DEFAULTCAPACITY_EMPTY_ELEMENTDATA
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* 預設大小的空例項共享的空陣列
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* ArrayList的元素儲存在其中的陣列緩衝區。ArrayList的容量是這個陣列緩衝區的長度。
* 當新增第一個元素時,任何為 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 的空ArrayList的容量
* 將擴充到預設大小DEFAULT_CAPACITY(10)。
* 設定為非private 是為了方便內部類進行訪問
*
* todo 內部動態陣列的維護
*/
transient Object[] elementData;
/**
* 列表中實際儲存的元素個數
*
* todo size 與 capacity
*/
private int size;
/**
* 構造一個指定初始容量的空列表
*/
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);
}
}
/**
* 構造一個預設容量的空列表
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* 構造一個包含集合C中所有元素的列表,儲存順序為集合C迭代器遍歷的順序
*/
public ArrayList(Collection extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
/**
* 調整容量大小到列表當前元素個數,以節約儲存空間
*/
public void trimToSize() {
//todo modCount的作用
modCount++;
if (size minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
/**
* 陣列容量最大值(- 8 是因為部分 JVM 需要在陣列中存入部分頭資訊)
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
* 擴容函式,擴容1.5 倍,擴容後的列表大小在minCapacity與1.5 倍原大小之間取最大值
*/
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
// >> 是右移運算子,作用結果是將原數值的二進位制數右移指定位數,轉換成十進位制的效果
// 就是除以2的指定次方,這裡右移一位即除以2
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity 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) {
if (minCapacity MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
/**
* 元素個數
*/
public int size() {
return size;
}
/**
* 是否為空
*/
public boolean isEmpty() {
return size == 0;
}
/**
* 是否包含某個元素
*/
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
/**
* 查詢元素第一次出現的位置
*/
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i = 0; i--)
if (elementData[i]==null)
return i;
} else {
for (int i = size-1; i >= 0; i--)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
/**
* 返回一個深克隆物件
*/
public Object clone() {
try {
// Object 的克隆方法,複製本物件及其內所有基本型別成員和 String 型別成員
// 但不會複製引用物件
ArrayList> v = (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(e);
}
}
/**
* 轉化成物件陣列
*/
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
/**
* 將列表轉存到陣列a中,如果a的空間足夠則直接存放,否則會新建一個陣列進行儲存
*/
@SuppressWarnings("unchecked")
public T[] toArray(T[] a) {
if (a.length size)
a[size] = null;
return a;
}
// 位置訪問操作
@SuppressWarnings("unchecked")
E elementData(int index) {
return (E) elementData[index];
}
/**
* 取序號為index的元素
*/
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
/**
* 替換指定位置的元素,並返回原來的元素
*/
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
/**
* 新增元素
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
/**
* 插入元素
*/
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
/**
* 移除指定序號的元素
*/
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; // clear to let GC do its work
return oldValue;
}
/**
* 移除某個元素
*/
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index 0)
//呼叫System.arraycopy 進行陣列複製
//四個引數分別是:待複製的陣列,原陣列的複製起始位置,複製到的目標陣列,目標陣列的起始位置
//這裡相當於把原陣列的一部分複製到另一部分,將陣列從index+1以後的部分整體往前移一個單位
//將index那個元素覆蓋掉
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
//然後將最後一個元素置為null
elementData[--size] = null; // clear to let GC do its work
}
/**
* 移除所有元素(將所有元素置為null,容量不變)
*/
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i c) {
//先將c轉換為陣列,然後再複製過來
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
/**
* 將集合C中的元素插入指定位置
*/
public boolean addAll(int index, Collection extends E> c) {
rangeCheckForAdd(index);
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
int numMoved = size - index;
if (numMoved > 0)
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}
/**
* 範圍性移除,移除列表裡序號從fromIndex到toIndex的所有元素,包含fromIndex,不包含toIndex
*/
protected void removeRange(int fromIndex, int toIndex) {
modCount++;
int numMoved = size - toIndex;
System.arraycopy(elementData, toIndex, elementData, fromIndex,
numMoved);
// 將其餘位置置為null
int newSize = size - (toIndex-fromIndex);
for (int i = newSize; i = size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
/**
* 插入時邊界檢測
*/
private void rangeCheckForAdd(int index) {
if (index > size || index c) {
Objects.requireNonNull(c);
return batchRemove(c, false);
}
/**
* 僅保留所有在集合C中的元素
*/
public boolean retainAll(Collection> c) {
Objects.requireNonNull(c);
return batchRemove(c, true);
}
private boolean batchRemove(Collection> c, boolean complement) {
final Object[] elementData = this.elementData;
int r = 0, w = 0;
boolean modified = false;
try {
for (; r 0) {
int capacity = calculateCapacity(elementData, size);
SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity);
ensureCapacityInternal(size);
Object[] a = elementData;
// Read in all elements in the proper order.
for (int i=0; i listIterator(int index) {
if (index size)
throw new IndexOutOfBoundsException("Index: "+index);
return new ListItr(index);
}
/**
* 返回一個ListIterator
*/
public ListIterator listIterator() {
return new ListItr(0);
}
/**
* 返回一個Iterator
*/
public Iterator iterator() {
return new Itr();
}
/**
* 最佳化版本的Iterator
* todo 迭代器中modCount的作用
*/
private class Itr implements Iterator {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
Itr() {}
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
public void remove() {
if (lastRet consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
/**
* 最佳化版的ListIterator
*/
private class ListItr extends Itr implements ListIterator {
ListItr(int index) {
super();
cursor = index;
}
public boolean hasPrevious() {
return cursor != 0;
}
public int nextIndex() {
return cursor;
}
public int previousIndex() {
return cursor - 1;
}
@SuppressWarnings("unchecked")
public E previous() {
checkForComodification();
int i = cursor - 1;
if (i = elementData.length)
throw new ConcurrentModificationException();
cursor = i;
return (E) elementData[lastRet = i];
}
public void set(E e) {
if (lastRet subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);
return new SubList(this, 0, fromIndex, toIndex);
}
/**
* 檢查傳入索引的合法性
*/
static void subListRangeCheck(int fromIndex, int toIndex, int size) {
if (fromIndex size)
throw new IndexOutOfBoundsException("toIndex = " + toIndex);
if (fromIndex > toIndex)
throw new IllegalArgumentException("fromIndex(" + fromIndex +
") > toIndex(" + toIndex + ")");
}
/**
* 內部類——子列表
*/
private class SubList extends AbstractList implements RandomAccess {
private final AbstractList parent;
private final int parentOffset;
private final int offset;
int size;
SubList(AbstractList parent,
int offset, int fromIndex, int toIndex) {
this.parent = parent;
this.parentOffset = fromIndex;
this.offset = offset + fromIndex;
this.size = toIndex - fromIndex;
this.modCount = ArrayList.this.modCount;
}
public E set(int index, E e) {
rangeCheck(index);
checkForComodification();
E oldValue = ArrayList.this.elementData(offset + index);
ArrayList.this.elementData[offset + index] = e;
return oldValue;
}
public E get(int index) {
rangeCheck(index);
checkForComodification();
return ArrayList.this.elementData(offset + index);
}
public int size() {
checkForComodification();
return this.size;
}
public void add(int index, E e) {
rangeCheckForAdd(index);
checkForComodification();
parent.add(parentOffset + index, e);
this.modCount = parent.modCount;
this.size++;
}
public E remove(int index) {
rangeCheck(index);
checkForComodification();
E result = parent.remove(parentOffset + index);
this.modCount = parent.modCount;
this.size--;
return result;
}
protected void removeRange(int fromIndex, int toIndex) {
checkForComodification();
parent.removeRange(parentOffset + fromIndex,
parentOffset + toIndex);
this.modCount = parent.modCount;
this.size -= toIndex - fromIndex;
}
public boolean addAll(Collection extends E> c) {
return addAll(this.size, c);
}
public boolean addAll(int index, Collection extends E> c) {
rangeCheckForAdd(index);
int cSize = c.size();
if (cSize==0)
return false;
checkForComodification();
parent.addAll(parentOffset + index, c);
this.modCount = parent.modCount;
this.size += cSize;
return true;
}
public Iterator iterator() {
return listIterator();
}
public ListIterator listIterator(final int index) {
checkForComodification();
rangeCheckForAdd(index);
final int offset = this.offset;
return new ListIterator() {
int cursor = index;
int lastRet = -1;
int expectedModCount = ArrayList.this.modCount;
public boolean hasNext() {
return cursor != ArrayList.SubList.this.size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= ArrayList.SubList.this.size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (offset + i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[offset + (lastRet = i)];
}
public boolean hasPrevious() {
return cursor != 0;
}
@SuppressWarnings("unchecked")
public E previous() {
checkForComodification();
int i = cursor - 1;
if (i = elementData.length)
throw new ConcurrentModificationException();
cursor = i;
return (E) elementData[offset + (lastRet = i)];
}
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.SubList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (offset + i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[offset + (i++)]);
}
// update once at end of iteration to reduce heap write traffic
lastRet = cursor = i;
checkForComodification();
}
public int nextIndex() {
return cursor;
}
public int previousIndex() {
return cursor - 1;
}
public void remove() {
if (lastRet subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);
return new ArrayList.SubList(this, offset, fromIndex, toIndex);
}
private void rangeCheck(int index) {
if (index = this.size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private void rangeCheckForAdd(int index) {
if (index this.size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private String outOfBoundsMsg(int index) {
return "Index: "+index+", Size: "+this.size;
}
private void checkForComodification() {
if (ArrayList.this.modCount != this.modCount)
throw new ConcurrentModificationException();
}
public Spliterator spliterator() {
checkForComodification();
return new ArrayList.ArrayListSpliterator(ArrayList.this, offset,
offset + this.size, this.modCount);
}
}
/**
* 遍歷
*/
@Override
public void forEach(Consumer super E> action) {
Objects.requireNonNull(action);
final int expectedModCount = modCount;
@SuppressWarnings("unchecked")
final E[] elementData = (E[]) this.elementData;
final int size = this.size;
for (int i=0; modCount == expectedModCount && i spliterator() {
return new ArrayList.ArrayListSpliterator(this, 0, -1, 0);
}
/** 基於索引的、二分的、懶載入的分割器*/
static final class ArrayListSpliterator implements Spliterator {
//用於存放ArrayList物件
private final ArrayList list;
//起始位置(包含),advance/split操作時會修改
private int index;
//結束位置(不包含),-1 表示到最後一個元素
private int fence;
//用於存放list的modCount,當fence被設值後初始化
private int expectedModCount;
/** 建立一個範圍性的分割器 */
ArrayListSpliterator(ArrayList list, int origin, int fence,
int expectedModCount) {
this.list = list; // OK if null unless traversed
this.index = origin;
this.fence = fence;
this.expectedModCount = expectedModCount;
}
//在第一次使用時例項化結束位置
private int getFence() {
int hi;
ArrayList lst;
if ((hi = fence) trySplit() {
int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
return (lo >= mid) ? null : // divide range in half unless too small
new ArrayList.ArrayListSpliterator(list, lo, index = mid,
expectedModCount);
}
/**
* 返回true 時,只表示可能還有元素未處理
* 返回false 時,沒有剩餘元素需要處理
*/
public boolean tryAdvance(Consumer super E> action) {
if (action == null)
throw new NullPointerException();
int hi = getFence(), i = index;
if (i action) {
int i, hi, mc; // hoist accesses and checks from loop
ArrayList lst; Object[] a;
if (action == null)
throw new NullPointerException();
if ((lst = list) != null && (a = lst.elementData) != null) {
if ((hi = fence) = 0 && (index = hi) filter) {
Objects.requireNonNull(filter);
// figure out which elements are to be removed
// any exception thrown from the filter predicate at this stage
// will leave the collection unmodified
int removeCount = 0;
final BitSet removeSet = new BitSet(size);
final int expectedModCount = modCount;
final int size = this.size;
for (int i=0; modCount == expectedModCount && i 0;
if (anyToRemove) {
final int newSize = size - removeCount;
for (int i=0, j=0; (i operator) {
Objects.requireNonNull(operator);
final int expectedModCount = modCount;
final int size = this.size;
for (int i=0; modCount == expectedModCount && i c) {
final int expectedModCount = modCount;
Arrays.sort((E[]) elementData, 0, size, c);
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
}
講真,我已經盡力了。
三、要點說明這部分主要根據上面的原始碼進行說明,如果有不太清楚的地方,可以返回上面的原始碼進行檢視。
1.ArrayList 其實只是內部維護了一個陣列,透過暴露出方便操作的介面來簡化操作。
2.ArrayList中,size和capacity是兩碼事,size表示列表中實際儲存的元素個數,一般小於內部陣列長度,而capacity表示容量,即內部陣列的長度。
3.ArrayList中,預設的大小是10,當你使用new ArrayList();時並不會立即為陣列分配大小為10的空間,而是等插入第一個元素時才會真正分配,這樣做是為了節約記憶體空間。
4.由於上述目的的存在,為了區分預設列表和空列表,設定了兩個空陣列常量,EMPTY_ELEMENTDATA和DEFAULTCAPACITY_EMPTY_ELEMENTDATA,這樣在擴容時就能進行不同的處理。
5.維護內部陣列時,使用的是Arrays.copyOf()方法和System.arraycopy()方法。
6.裡面有多處使用modCount,這個變數其實是繼承自父類AbstractList,用來標識列表內部陣列大小被修改的次數(如add,trimToSize等操作可能會觸發),元素的替換並不會改變它的值,迭代器的“fail-fast”機制跟這個modCount變數緊密相關,一般會在操作前賦值一次 expectedModCount = modCount; 在操作執行完之後再進行一次檢測,如果仍相等,說明結構未改變,否則將丟擲異常,這也就是為什麼上一篇中ArrayList修改過之後,操作迭代器會丟擲異常的原因。
7.在擴容時,預設的擴容因子是1.5,每次需要擴容時,會將原陣列大小的1.5倍和實際需要的陣列空間進行比較,從中取最大值作為陣列大小。然後新建一個陣列,把原陣列中的所有元素複製到新陣列中去。所以擴容其實是最耗費時間的操作,不僅僅需要重新分配空間,而且需要重新賦值。
8.因為ArrayList的方法操作的都是同一個內部陣列,而所有方法都沒有加鎖,沒有同步機制,所以它是執行緒不安全的。
9.ArrayList中可以存放null值,可以在原始碼中看到,在比較時對null值都進行了處理。
四、優缺點說明因為列表其實是內部維護管理著一個陣列,所以陣列的優點它都具備。當然,陣列的缺點它同樣也存在。
陣列是將元素在記憶體中連續存放,由於每個元素佔用記憶體相同,可以透過下標迅速訪問陣列中任何元素。但是如果要在陣列中增加一個元素,需要移動大量元素,在記憶體中空出一個元素的空間,然後將要增加的元素放在其中。同樣的道理,如果想刪除一個元素,同樣需要移動大量元素去填掉被移動的元素。
但是列表在維護這個內部陣列時,還是花了一點心思的,比如使用capacity的概念來減少陣列結構改變的次數,所以並不會每次add操作都導致結構改變。將擴容因子選為1.5而不是2,也是為了在滿足需求的前提下儘可能的節約空間,但如果事先就知道元素的大概個數時,最好先在構造器中設定好列表的容量,這樣就可以省掉不少擴容時的開銷。
呼。這篇準備了兩天才搞定,希望能夠幫助到大家,如果有什麼遺漏或者講的不夠清晰的地方,歡迎指出,如果有說錯的知識點,也請不要吝嗇,歡迎指正。
看在我這樣辛勤耕作的份上,動動小手點個贊吧,也歡迎關注我的部落格,後續還會持續更新。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4479/viewspace-2802049/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 【Java入門提高篇】Day24 Java容器類詳解(七)HashMaJava
- Java 集合類入門篇Java
- 詳解Java 容器(第①篇)——概覽Java
- 詳解Java 容器(第③篇)——容器原始碼分析 - ListJava原始碼
- 詳解Java 容器(第④篇)——容器原始碼分析 - MapJava原始碼
- 詳解Java 容器(第⑤篇)——容器原始碼分析 - 併發容器Java原始碼
- Java容器詳解Java
- 詳解Java 容器(第②篇)——容器中的設計模式Java設計模式
- Java類載入器詳解Java
- Java 8 中新的 Date 和 Time 類入門詳解Java
- 詳解Java 容器(完結篇)——詳解容器的設計模式、List、Map、併發容器Java設計模式
- 【Java入門提高篇】Day16 Java異常處理(上)Java
- Java 類載入機制詳解Java
- Java類載入機制詳解【java面試題】Java面試題
- Java反射詳解篇--一篇入魂Java反射
- Java原始碼篇之容器類——ArrayListJava原始碼
- 【Java】Java容器篇(二),深入理解List集合類Java
- Java面試題之Java類載入機制詳解!Java面試題
- java 容器類Java
- 類與介面(二)java的四種內部類詳解Java
- Java Class類詳解Java
- [Java]File類詳解Java
- [Java]RandomAccessFile類詳解JavarandomMac
- Java集合類詳解Java
- Java學習筆記-Day21 Java System類、Class類、內部類、異常處理Java筆記
- Java基礎篇—Java類載入機制Java
- Java內部類詳解Java
- Java 內部類詳解Java
- java刷題時常用容器詳解Java
- Java內部類入門示例Java
- java入門 -- Java I/O(四) 異常處理Java
- Java集合(四) LinkedList詳解Java
- Java入門教程四(字串處理)Java字串
- Java容器工具類ArraysJava
- Java GUI入門手冊-AWT篇JavaGUI
- java 入門篇 問題集錦Java
- Java程式碼審計入門篇Java
- Java 內部類使用詳解Java