- 成員變量
- 序列化號(hào)
private static final long serialVersionUID = 8683452581122892189L;
2.默認(rèn)初始化容量 10
private static final int DEFAULT_CAPACITY = 10;
3.用于空實(shí)例的共享空數(shù)組實(shí)例
當(dāng)ArrayList的構(gòu)造方法中顯示指出ArrayList的數(shù)組長(zhǎng)度為0時(shí),類內(nèi)部將
EMPTY_ELEMENTDATA
這個(gè)空對(duì)象數(shù)組賦給elemetData數(shù)組。
private static final Object[] EMPTY_ELEMENTDATA = {};
4.用于默認(rèn)大小的空實(shí)例的共享空數(shù)組實(shí)例。
將其與EMPTY_ELEMENTDATA
區(qū)分開來檀轨,以了解在添加第一個(gè)元素時(shí)應(yīng)該擴(kuò)充多少略贮。當(dāng)ArrayList的構(gòu)造方法中沒有顯示指出ArrayList的數(shù)組長(zhǎng)度時(shí)兄淫,類內(nèi)部使用默認(rèn)缺省時(shí)對(duì)象數(shù)組為DEFAULTCAPACITY_EMPTY_ELEMENTDATA
绑莺。
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
5.存儲(chǔ)ArrayList元素的數(shù)組緩沖區(qū)。
ArrayList的容量是這個(gè)數(shù)組緩沖區(qū)的長(zhǎng)度哩至。當(dāng)添加第一個(gè)元素時(shí)躏嚎,任何帶有elementData
==DEFAULTCAPACITY_EMPTY_ELEMENTDATA
的空ArrayList都將被擴(kuò)展為DEFAULT_CAPACITY
。
ArrayList的底層數(shù)據(jù)結(jié)構(gòu)憨募,只是一個(gè)對(duì)象數(shù)組紧索,用于存放實(shí)際元素,并且被標(biāo)記為transient
菜谣,也就意味著在序列化的時(shí)候此字段是不會(huì)被序列化
transient Object[] elementData;
6.大小
實(shí)際ArrayList中存放的元素的個(gè)數(shù)珠漂,默認(rèn)時(shí)為0個(gè)元素晚缩。
private int size;
7.要分配的數(shù)組的最大大小。
一些jvm在數(shù)組中保留一些標(biāo)題詞媳危。
試圖分配更大的數(shù)組可能會(huì)導(dǎo)致OutOfMemoryError:
請(qǐng)求的數(shù)組大小超過VM限制
java int 的最大值Integer.MAX_VALUE
是2147483647
ArrayList中的對(duì)象數(shù)組的最大數(shù)組容量為Integer.MAX_VALUE – 8
荞彼。
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
- 構(gòu)造方法
1 無參構(gòu)造
/**
* Constructs an empty list with an initial capacity of ten.
* 構(gòu)造一個(gè)初始容量為10的空列表。
* 在該方法中沒有對(duì)數(shù)組長(zhǎng)度進(jìn)行設(shè)置待笑,后續(xù)在add方法中才去進(jìn)行擴(kuò)容
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
2 有參構(gòu)造_傳入容量大小
/**
* Constructs an empty list with the specified initial capacity.
* 構(gòu)造具有初始化容量的空列表
* @param initialCapacity the initial capacity of the list
* 列表的初始化容量
* @throws IllegalArgumentException if the specified initial capacity
* is negative
*/
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) { // 初始容量大于0
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
// 將EMPTY_ELEMENTDATA空對(duì)象數(shù)組賦給elementData鸣皂。
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
3 有參構(gòu)造_傳入集合
/**
* Constructs a list containing the elements of the specified
* collection, in the order they are returned by the collection's
* iterator.
* 構(gòu)造一個(gè)集合,其中包含指定集合的元素暮蹂,按集合的迭代器返回元素的順序排列寞缝。
* @param c the collection whose elements are to be placed into this list
* 要將其元素放入此列表的集合
* @throws NullPointerException if the specified collection is null
*/
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
// 這個(gè)注釋,參見bug6260652仰泻,toArray方法確實(shí)有時(shí)候返回的不是Obdect[]
// List<String> strList= Arrays.asList("abc");返回的結(jié)果是String[]
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
解析:
第一步荆陆,將參數(shù)中的集合轉(zhuǎn)化為數(shù)組賦給elementData;
第二步集侯,判斷參數(shù)集合轉(zhuǎn)換后的數(shù)組長(zhǎng)度是否為0被啼。并把該數(shù)組長(zhǎng)度的大小賦值給size。
第三步棠枉,如果該數(shù)組長(zhǎng)度為0浓体,則設(shè)置元素?cái)?shù)組為空,即將EMPTY_ELEMENTDATA空對(duì)象數(shù)組賦給elementData辈讶;
第四步命浴,如果該數(shù)組長(zhǎng)度不為0,接下來判斷是否成功將參數(shù)集合轉(zhuǎn)化為Object類型的數(shù)組荞估,
如果轉(zhuǎn)化成Object類型的數(shù)組成功咳促,則將數(shù)組進(jìn)行復(fù)制,轉(zhuǎn)化為Object類型的數(shù)組勘伺。
- 方法
1 調(diào)整容量大小
將這個(gè)ArrayList實(shí)例的容量調(diào)整為列表的當(dāng)前大小。
可以使用此操作最小化ArrayList實(shí)例的存儲(chǔ)褂删。
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
//elementData:要復(fù)制的數(shù)組飞醉;size:要復(fù)制的長(zhǎng)度
: Arrays.copyOf(elementData, size);
}
}
這個(gè)modCount是父類抽象類的方法,防止在迭代過程中并發(fā)修改的不確定性行為屯阀。
2.預(yù)先設(shè)置list的大小
每當(dāng)向數(shù)組中添加元素時(shí)缅帘,都要去檢查添加后元素的個(gè)數(shù)是否會(huì)超出當(dāng)前數(shù)組的長(zhǎng)度,
如果超出难衰,數(shù)組將會(huì)進(jìn)行擴(kuò)容钦无,以滿足添加數(shù)據(jù)的需求。
每次數(shù)組容量的增長(zhǎng)大約是其原容量的1.5倍盖袭。對(duì)數(shù)組擴(kuò)容是要進(jìn)行數(shù)組拷貝的失暂,這就會(huì)浪費(fèi)大量的時(shí)間彼宠。如果已經(jīng)預(yù)知容器可能會(huì)裝多少元素,最好顯式的調(diào)用ensureCapacity這個(gè)方法一次性擴(kuò)容到位弟塞。
public void ensureCapacity(int minCapacity) {
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
// any size if not default element table
// 任何大小凭峡,如果不是默認(rèn)的元素表
// 判斷需要擴(kuò)容的數(shù)組是否為默認(rèn)空實(shí)例(空數(shù)組)
// 如果不為默認(rèn)空實(shí)例,則變量等于0.為默認(rèn)空實(shí)例則變量等于數(shù)組默認(rèn)容量 10
? 0
// larger than default for default empty table. It's already
// supposed to be at default size.
: DEFAULT_CAPACITY;
// 如果該數(shù)組是默認(rèn)空實(shí)例决记,則指定變量minExpand = DEFAULT_CAPACITY:10
// 因此摧冀,當(dāng)期望擴(kuò)容的量小于10 的時(shí)候,就不需要擴(kuò)容
// 因?yàn)槟J(rèn)空實(shí)例的默認(rèn)容量就是10系宫,已經(jīng)滿足期望擴(kuò)容的量
if (minCapacity > minExpand) {
// 如果需要擴(kuò)容的量大于定義的變量索昂,則繼續(xù)判斷是否需要擴(kuò)容
ensureExplicitCapacity(minCapacity);
}
}
3 添加方法
將指定的元素附加到此列表的末尾。
public boolean add(E e) {
// 確保添加元素成功的最小集合容量
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
// 確定集合內(nèi)部容量扩借。
// 這個(gè)方法很有意思楼镐,先計(jì)算容量大小,再確定是否要進(jìn)行擴(kuò)容往枷,
// 最終達(dá)成預(yù)先設(shè)置list的大小框产。與2方法相似
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
// 計(jì)算容量
// 如果當(dāng)前集合elementData對(duì)象是默認(rèn)空實(shí)例DEFAULTCAPACITY_EMPTY_ELEMENTDATA
// 則將參數(shù)minCapacity和DEFAULT_CAPACITY比較,取最大
// 否則取參數(shù)minCapacity
private static int calculateCapacity(Object[] elementData, int minCapacity) {
// 如果這個(gè)elementData 是默認(rèn)大小的空實(shí)例的共享空數(shù)組實(shí)例错洁,
// 那么參數(shù)minCapacity容量和默認(rèn)值10比較秉宿,取最大值
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
// 否則取參數(shù)minCapacity
return minCapacity;
}
// 確定是否需要擴(kuò)容
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
// 如果指定容量大于當(dāng)前集合的長(zhǎng)度,就要擴(kuò)容
if (minCapacity - elementData.length > 0)
// 真正的擴(kuò)容方法
grow(minCapacity);
}
// 真正的擴(kuò)容方法
private void grow(int minCapacity) {
// overflow-conscious code 獲得舊的容量大小值
int oldCapacity = elementData.length;
// 新的容量 = 舊的容量 + (舊的容量 / 2) 也就是大概擴(kuò)容1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 如果新的容量比期望容量小屯碴,那么期望容量為新的容量
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 如果新的容量超過最大范圍描睦,則調(diào)用hugeCapacity方法去決定新的容量的大小
// private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
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);
}
// 確保不小于0并且不超過最大容量
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
//Integer.MAX_VALUE - 8;
MAX_ARRAY_SIZE;
}
/**
* Arrays的方法
* Copies the specified array, truncating or padding with nulls (if necessary)
* so the copy has the specified length. For all indices that are
* valid in both the original array and the copy, the two arrays will
* contain identical values. For any indices that are valid in the
* copy but not the original, the copy will contain <tt>null</tt>.
* Such indices will exist if and only if the specified length
* is greater than that of the original array.
* The resulting array is of exactly the same class as the original array.
*
* @param <T> the class of the objects in the array
* @param original the array to be copied
* @param newLength the length of the copy to be returned
* @return a copy of the original array, truncated or padded with nulls
* to obtain the specified length
* @throws NegativeArraySizeException if <tt>newLength</tt> is negative
* @throws NullPointerException if <tt>original</tt> is null
* @since 1.6
*/
@SuppressWarnings("unchecked")
public static <T> T[] copyOf(T[] original, int newLength) {
return (T[]) copyOf(original, newLength, original.getClass());
}
/**
* Arrays的方法
* Copies the specified array, truncating or padding with nulls (if necessary)
* so the copy has the specified length. For all indices that are
* valid in both the original array and the copy, the two arrays will
* contain identical values. For any indices that are valid in the
* copy but not the original, the copy will contain <tt>null</tt>.
* Such indices will exist if and only if the specified length
* is greater than that of the original array.
* The resulting array is of the class <tt>newType</tt>.
*
* @param <U> the class of the objects in the original array
* @param <T> the class of the objects in the returned array
* @param original the array to be copied
* @param newLength the length of the copy to be returned
* @param newType the class of the copy to be returned
* @return a copy of the original array, truncated or padded with nulls
* to obtain the specified length
* @throws NegativeArraySizeException if <tt>newLength</tt> is negative
* @throws NullPointerException if <tt>original</tt> is null
* @throws ArrayStoreException if an element copied from
* <tt>original</tt> is not of a runtime type that can be stored in
* an array of class <tt>newType</tt>
* @since 1.6
*/
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);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
/**
* System的方法
* Copies an array from the specified source array, beginning at the
* specified position, to the specified position of the destination array.
* A subsequence of array components are copied from the source
* array referenced by <code>src</code> to the destination array
* referenced by <code>dest</code>. The number of components copied is
* equal to the <code>length</code> argument. The components at
* positions <code>srcPos</code> through
* <code>srcPos+length-1</code> in the source array are copied into
* positions <code>destPos</code> through
* <code>destPos+length-1</code>, respectively, of the destination
* array.
* <p>
* If the <code>src</code> and <code>dest</code> arguments refer to the
* same array object, then the copying is performed as if the
* components at positions <code>srcPos</code> through
* <code>srcPos+length-1</code> were first copied to a temporary
* array with <code>length</code> components and then the contents of
* the temporary array were copied into positions
* <code>destPos</code> through <code>destPos+length-1</code> of the
* destination array.
* <p>
* If <code>dest</code> is <code>null</code>, then a
* <code>NullPointerException</code> is thrown.
* <p>
* If <code>src</code> is <code>null</code>, then a
* <code>NullPointerException</code> is thrown and the destination
* array is not modified.
* <p>
* Otherwise, if any of the following is true, an
* <code>ArrayStoreException</code> is thrown and the destination is
* not modified:
* <ul>
* <li>The <code>src</code> argument refers to an object that is not an
* array.
* <li>The <code>dest</code> argument refers to an object that is not an
* array.
* <li>The <code>src</code> argument and <code>dest</code> argument refer
* to arrays whose component types are different primitive types.
* <li>The <code>src</code> argument refers to an array with a primitive
* component type and the <code>dest</code> argument refers to an array
* with a reference component type.
* <li>The <code>src</code> argument refers to an array with a reference
* component type and the <code>dest</code> argument refers to an array
* with a primitive component type.
* </ul>
* <p>
* Otherwise, if any of the following is true, an
* <code>IndexOutOfBoundsException</code> is
* thrown and the destination is not modified:
* <ul>
* <li>The <code>srcPos</code> argument is negative.
* <li>The <code>destPos</code> argument is negative.
* <li>The <code>length</code> argument is negative.
* <li><code>srcPos+length</code> is greater than
* <code>src.length</code>, the length of the source array.
* <li><code>destPos+length</code> is greater than
* <code>dest.length</code>, the length of the destination array.
* </ul>
* <p>
* Otherwise, if any actual component of the source array from
* position <code>srcPos</code> through
* <code>srcPos+length-1</code> cannot be converted to the component
* type of the destination array by assignment conversion, an
* <code>ArrayStoreException</code> is thrown. In this case, let
* <b><i>k</i></b> be the smallest nonnegative integer less than
* length such that <code>src[srcPos+</code><i>k</i><code>]</code>
* cannot be converted to the component type of the destination
* array; when the exception is thrown, source array components from
* positions <code>srcPos</code> through
* <code>srcPos+</code><i>k</i><code>-1</code>
* will already have been copied to destination array positions
* <code>destPos</code> through
* <code>destPos+</code><i>k</I><code>-1</code> and no other
* positions of the destination array will have been modified.
* (Because of the restrictions already itemized, this
* paragraph effectively applies only to the situation where both
* arrays have component types that are reference types.)
*
* @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.
* @exception IndexOutOfBoundsException if copying would cause
* access of data outside array bounds.
* @exception ArrayStoreException if an element in the <code>src</code>
* array could not be stored into the <code>dest</code> array
* because of a type mismatch.
* @exception NullPointerException if either <code>src</code> or
* <code>dest</code> is <code>null</code>.
*/
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
擴(kuò)容機(jī)制:
第一导而,在add()方法中調(diào)用ensureCapacityInternal(size + 1)方法來確定集合確保添加元素成功的最小集合容量minCapacity的值忱叭。參數(shù)size+1,代表集合添加元素成功后今艺,集合中的實(shí)際元素個(gè)數(shù)韵丑。
在ensureCapacityInternal方法中,首先判斷elementData是否為默認(rèn)的空數(shù)組虚缎,如果是撵彻,minCapacity為minCapacity與集合默認(rèn)容量大小中的較大值。否則直接返回minCapacity
第二实牡,調(diào)用ensureExplicitCapacity(minCapacity)方法來確定集合為了確保添加元素成功是否需要對(duì)現(xiàn)有的元素?cái)?shù)組進(jìn)行擴(kuò)容陌僵。首先將結(jié)構(gòu)性修改計(jì)數(shù)器modCount加一;然后判斷minCapacity與當(dāng)前元素?cái)?shù)組的長(zhǎng)度的大小创坞,如果minCapacity比當(dāng)前元素?cái)?shù)組的長(zhǎng)度的大的時(shí)候則需要擴(kuò)容碗短,進(jìn)入第三階段。
第三题涨,如果需要對(duì)現(xiàn)有的元素?cái)?shù)組進(jìn)行擴(kuò)容偎谁,則調(diào)用grow(minCapacity)方法总滩,參數(shù)minCapacity表示集合為了確保添加元素成功的最小容量。在擴(kuò)容的時(shí)候搭盾,首先將原元素?cái)?shù)組的長(zhǎng)度增大1.5倍(oldCapacity + (oldCapacity >> 1))咳秉,然后對(duì)擴(kuò)容后的容量與minCapacity進(jìn)行比較:
①新容量小于minCapacity,則將minCapacity設(shè)為新容量鸯隅;
②新容量大于minCapacity澜建,則指定新容量。
③最后將舊數(shù)組拷貝到擴(kuò)容后的新數(shù)組中蝌以。
- 下面分析用無參構(gòu)造函數(shù)實(shí)例并添加元素都做了些什么
- List list = new ArrayList<>();
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* Constructs an empty list with an initial capacity of ten.
* 構(gòu)造一個(gè)初始容量為10的空列表炕舵。
* 在該方法中沒有對(duì)數(shù)組長(zhǎng)度進(jìn)行設(shè)置,后續(xù)在add方法中才去進(jìn)行擴(kuò)容
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
首先是先構(gòu)建一個(gè)初始容量為10的空的列表跟畅,即:this.elementData = {}咽筋;
- list.add("a");
再次把代碼貼下來方便查看
private int size;
public boolean add(E e) { // e = "a"
// 第一步:確保添加元素成功的最小集合容量
ensureCapacityInternal(size + 1); // size + 1 = 0 + 1 = 1
elementData[size++] = e;
return true;
}
// 第二步:確定集合內(nèi)部容量。
private void ensureCapacityInternal(int minCapacity) { // minCapacity = 1
// ensureExplicitCapacity(calculateCapacity( {} , 1 ));
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
// 第三步:計(jì)算容量
private static int calculateCapacity(Object[] elementData, int minCapacity) { // {} , 1
// 這個(gè)elementData 是默認(rèn)大小的空實(shí)例的共享空數(shù)組實(shí)例 {}徊件,
// DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
// DEFAULT_CAPACITY = 10, minCapacity = 1;
// 所以指定的minCapacity容量和默認(rèn)值10比較奸攻,取最大值 10
// 并返回給 ensureExplicitCapacity
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
// 否則取傳入的minCapacity
return minCapacity;
}
// 第四步:確定是否需要擴(kuò)容
// 拿到 minCapacity = calculateCapacity 返回的 10
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
// 指定容量大于當(dāng)前集合的長(zhǎng)度,所以要擴(kuò)容
if (minCapacity - elementData.length > 0) // 10 - 0
// 真正的擴(kuò)容方法
grow(minCapacity); // minCapacity = 10
}
// 第五步:真正的擴(kuò)容方法
private void grow(int minCapacity) { // 擴(kuò)容指定容量 minCapacity = 10
// overflow-conscious code 獲得舊的容量大小值
int oldCapacity = elementData.length; // 該元素是空集合虱痕,所以長(zhǎng)度是 0;
// 新的容量 = 舊的容量 + (舊的容量 / 2) 因?yàn)閷?shí)際上是位運(yùn)算睹耐,即大概擴(kuò)容1.5倍
// newCapacity = 0 + (0 / 2) = 0;
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 新的容量 0 比期望容量 10 小,那么期望容量 10 為新的容量
if (newCapacity - minCapacity < 0) // 0 - 10
newCapacity = minCapacity; // newCapacity = 10
// 如果新的容量超過最大范圍部翘,則調(diào)用hugeCapacity方法去決定新的容量的大小
// private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
// 10 - Integer.MAX_VALUE - 8;肯定是小于0硝训,所以不走這個(gè)方法體
if (newCapacity - MAX_ARRAY_SIZE > 0)
// 注意傳參的是指定容量,也就是指定容量與最大容量比較
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
// 最后走到這個(gè)方法:調(diào)用 Arrays.copyOf 方法進(jìn)行數(shù)組拷貝新思。
elementData = Arrays.copyOf(elementData, newCapacity); // {}窖梁,10
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
//Integer.MAX_VALUE - 8;
MAX_ARRAY_SIZE;
}
// Arrays的copyOf方法
public static <T> T[] copyOf(T[] original, int newLength) { // {},10
// copyOf( { }, 10, Object類型 );
return (T[]) copyOf(original, newLength, original.getClass());
}
public static <T,U> T[] copyOf(U[] original, int newLength,
Class<? extends T[]> newType) { // { }, 10, Object類型
@SuppressWarnings("unchecked")
T[] copy = ((Object)newType == (Object)Object[].class) // 是Object類型夹囚,所以能夠轉(zhuǎn)換成功
// 因此創(chuàng)建新的數(shù)組纵刘,且指定長(zhǎng)度為10
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
// 然后走 System.arraycopy 方法,舊數(shù)組元素拷貝到新數(shù)組上
// System.arraycopy({}, 0, new Object[10], 0, Math.min(0, 10));
// 截取的個(gè)數(shù)這樣取值 Math.min(original.length, newLength)
// 表示要同時(shí)滿足:1 源數(shù)組的總數(shù)范圍內(nèi)崔兴,2 目標(biāo)數(shù)組的容量范圍內(nèi)
// 如果源數(shù)組的總數(shù)長(zhǎng)過目標(biāo)數(shù)組的容量彰导,源數(shù)組超出的部分將不拷貝
// 如果目標(biāo)數(shù)組的容量大于源數(shù)組的總數(shù),目標(biāo)數(shù)組超出的部分為null
// 如果目標(biāo)數(shù)組原本有值敲茄,則超出部分的值不變
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
// System.arraycopy 方法
// System.arraycopy({}, 0, new Object[10], 0, 0);
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
System.arraycopy 方法解釋:
src:源數(shù)組;
srcPos:源數(shù)組要復(fù)制的起始位置山析;
dest:目的數(shù)組堰燎;
destPos:目的數(shù)組放置的起始位置;
length:復(fù)制的長(zhǎng)度笋轨。
注意:src and dest都必須是同類型或者可以進(jìn)行轉(zhuǎn)換類型的數(shù)組.
比如 :
int arry1[] = { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };
int arry2[] = { 15, 25, 35, 45, 55, 65, 75, 85, 95, 105 };
source_arr = arry1;
sourcePos = 3;
dest_arr = arry2;
destPos = 5;
len = 4;
System.arraycopy(source_arr, sourcePos, dest_arr, destPos, len);
// System.arraycopy(arry1, 3, arry2, 5, 4);
// 意思是截取arry1從下標(biāo)為3開始4個(gè)元素:40, 50, 60, 70
// 把它們放到arry2的下標(biāo)為5的位置及其后面(覆蓋掉原索引位的值)
System.out.print("final dest_array : ");
for (int i = 0; i < arry2.length; i++)
System.out.print(arry2[i] + " ");
}
結(jié)果:
final dest_array : 15 25 35 45 55 40 50 60 70 105
4 獲取當(dāng)前集合大小
public int size() {
return size;
}
5 判斷集合是否為空
public boolean isEmpty() {
return size == 0;
}
6 判斷元素對(duì)象是否存在集合當(dāng)中
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
7 從頭開始循環(huán)遍歷秆剪,找到集合相同的元素
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
8 尾部循環(huán)遍歷赊淑,找到集合相同的元素
public int lastIndexOf(Object o) {
if (o == null) {
for (int i = size-1; 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;
}
9 淺拷貝(元素本身不會(huì)被復(fù)制)
public Object clone() {
try {
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);
}
}
10 集合轉(zhuǎn)換成數(shù)組_無參
// 和擴(kuò)容方法grow最后調(diào)用的方法是相同的:Arrays.copyOf(elementData, newCapacity);
// 但是傳入的參數(shù)不同,grow方法傳入的是指定大小仅讽,它傳入的是元素實(shí)際個(gè)數(shù)size
// 返回的數(shù)組將是“安全的”陶缺,因?yàn)閠oArray()返回的是一個(gè)新的數(shù)組對(duì)象,List不維護(hù)對(duì)它的引用洁灵。
// 換句話說饱岸,這個(gè)方法必須分配一個(gè)新數(shù)組。
// 因此徽千,調(diào)用者可以自由地修改返回的數(shù)組苫费,
// 不會(huì)影響到list本身原來存儲(chǔ)的元素值,并且不會(huì)影響到其他toArray()方法獲得的數(shù)組双抽。
// 此方法充當(dāng)基于數(shù)組和基于集合的api之間的橋梁百框。
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
但是,如果該集合里面有其它自定義類型的元素牍汹,例如一個(gè)person類铐维,如果對(duì)這個(gè)對(duì)象元素內(nèi)容修改,則會(huì)影響到List本身的內(nèi)容慎菲,導(dǎo)致toArray()返回的所有數(shù)組中的內(nèi)容都發(fā)生改變(原理參考Cloneable接口的深淺拷貝嫁蛇,即List存儲(chǔ)的只是該對(duì)象的地址指針,實(shí)際并沒有對(duì)該對(duì)象進(jìn)行拷貝)
- 再貼出拷貝方法具體實(shí)現(xiàn)
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
public static <T> T[] copyOf(T[] original, int newLength) {
return (T[]) copyOf(original, newLength, original.getClass());
}
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);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
public static Object newInstance(Class<?> componentType, int length)
throws NegativeArraySizeException {
return newArray(componentType, length);
}
private static native Object newArray(Class<?> componentType, int length)
throws NegativeArraySizeException;
System.arraycopy 方法解釋:
src:源數(shù)組钧嘶;
srcPos:源數(shù)組要復(fù)制的起始位置棠众;
dest:目的數(shù)組;
destPos:目的數(shù)組放置的起始位置有决;
length:復(fù)制的長(zhǎng)度闸拿。
注意:src and dest都必須是同類型或者可以進(jìn)行轉(zhuǎn)換類型的數(shù)組.
比如 :
int arry1[] = { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };
int arry2[] = { 15, 25, 35, 45, 55, 65, 75, 85, 95, 105 };
source_arr = arry1;
sourcePos = 3;
dest_arr = arry2;
destPos = 5;
len = 4;
System.arraycopy(source_arr, sourcePos, dest_arr, destPos, len);
// System.arraycopy(arry1, 3, arry2, 5, 4);
// 意思是截取arry1從下標(biāo)為3開始4個(gè)元素:40, 50, 60, 70
// 把它們放到arry2的下標(biāo)為5的位置及其后面(覆蓋掉原索引位的值)
System.out.print("final dest_array : ");
for (int i = 0; i < arry2.length; i++)
System.out.print(arry2[i] + " ");
}
結(jié)果:
final dest_array : 15 25 35 45 55 40 50 60 70 105
11 集合轉(zhuǎn)換成數(shù)組_有參
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
// 數(shù)組長(zhǎng)度小于集合長(zhǎng)度,會(huì)創(chuàng)建一個(gè)與集合長(zhǎng)度相同的新數(shù)組
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
// 數(shù)組長(zhǎng)度大于等于集合長(zhǎng)度书幕,直接拷貝新荤,不產(chǎn)生新數(shù)組對(duì)象;
if (a.length > size)
// 在a[size]放置一個(gè)null台汇,size就是list集合中元素的個(gè)數(shù)苛骨,
// 這個(gè)null值可以使得toArray(T[] a)方法調(diào)用者可以判斷null后面已經(jīng)沒有l(wèi)ist元素了。
a[size] = null;
// 數(shù)組長(zhǎng)度等于集合長(zhǎng)度
return a;
}
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);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
兩個(gè)方法簡(jiǎn)單總結(jié):
1.toArray()無參方法苟呐,可以直接將集合轉(zhuǎn)換成Object數(shù)組進(jìn)行返回(返回List中所有元素構(gòu)成的數(shù)組痒芝,并且數(shù)組類型是Object[]。)
2.toArray(T[] a)有參方法牵素,需要傳入一個(gè)數(shù)組作為參數(shù)严衬,并通過泛型T的方式定義參數(shù),所返回的數(shù)組類型就是調(diào)用集合者的泛型笆呆,所以自己無需再轉(zhuǎn)型请琳;但是根據(jù)傳入數(shù)組的長(zhǎng)度與集合的實(shí)際長(zhǎng)度的關(guān)系粱挡,會(huì)有不同的處理;
a:數(shù)組長(zhǎng)度不小于集合長(zhǎng)度俄精,直接拷貝询筏,不會(huì)產(chǎn)生新的數(shù)組對(duì)象;
b:數(shù)組長(zhǎng)度小于集合長(zhǎng)度竖慧,會(huì)創(chuàng)建一個(gè)與集合長(zhǎng)度相同的新數(shù)組撩匕,將集合的數(shù)據(jù)拷貝到新數(shù)組并將新數(shù)組的引用返回憔辫。如果a數(shù)組還有剩余的空間,則在a[size]放置一個(gè)null,size就是list中元素的個(gè)數(shù)辐啄,這個(gè)null值可以使得toArray(T[] a)方法調(diào)用者可以判斷null后面已經(jīng)沒有l(wèi)ist元素了楣导。
c:兩種情況的拷貝方式是一樣的烦租,只是有一種情況會(huì)產(chǎn)生新的數(shù)組實(shí)例
12 位置訪問
E elementData(int index) {
return (E) elementData[index];
}
13 返回指定位置的元素
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
14 將集合中指定位置的元素替換為指定元素豺鼻。返回的是被修改前的元素
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
15 將指定元素插入到列表中的指定位置。如果該索引處以及后面都有元素挨约,那么將它們索引都往后移一位
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
// elementData = Arrays.copyOf(elementData, newCapacity); // size + 1
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
// 由add和addAll使用的rangeCheck的一個(gè)版本味混。
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
// System.arraycopy是一個(gè)原生的方法,用于數(shù)組間的復(fù)制诫惭,延伸功能完成數(shù)組替換
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
5個(gè)參數(shù)翁锡,
第一個(gè)參數(shù)是要被復(fù)制的數(shù)組
第二個(gè)參數(shù)是被復(fù)制的數(shù)字開始復(fù)制的下標(biāo)
第三個(gè)參數(shù)是目標(biāo)數(shù)組,也就是要把數(shù)據(jù)放進(jìn)來的數(shù)組
第四個(gè)參數(shù)是從目標(biāo)數(shù)據(jù)第幾個(gè)下標(biāo)開始放入數(shù)據(jù)
第五個(gè)參數(shù)表示從被復(fù)制的數(shù)組中拿幾個(gè)數(shù)值放到目標(biāo)數(shù)組中
- 再貼出確保容量的具體實(shí)現(xiàn)
// 確定集合內(nèi)部容量夕土。
// 這個(gè)方法很有意思馆衔,先計(jì)算容量大小,再確定是否要進(jìn)行擴(kuò)容怨绣,
// 最終達(dá)成預(yù)先設(shè)置list的大小角溃。與2方法相似(預(yù)先設(shè)置list的大小)
private void ensureCapacityInternal(int minCapacity) { // size + 1
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
// 計(jì)算容量
// 如果當(dāng)前集合elementData對(duì)象是默認(rèn)空實(shí)例DEFAULTCAPACITY_EMPTY_ELEMENTDATA
// 則將參數(shù)minCapacity和DEFAULT_CAPACITY比較,取最大
// 否則取參數(shù)minCapacity
private static int calculateCapacity(Object[] elementData, int minCapacity) {
// 如果這個(gè)elementData 是默認(rèn)大小的空實(shí)例的共享空數(shù)組實(shí)例篮撑,
// 那么參數(shù)minCapacity容量和默認(rèn)值10比較减细,取最大值
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
// 否則取參數(shù)minCapacity
return minCapacity; // size + 1
}
// 確定是否需要擴(kuò)容
private void ensureExplicitCapacity(int minCapacity) { // size + 1
modCount++;
// overflow-conscious code
// 如果指定容量大于當(dāng)前集合的長(zhǎng)度,就要擴(kuò)容
if (minCapacity - elementData.length > 0)
// 真正的擴(kuò)容方法
grow(minCapacity); // size + 1
}
// 真正的擴(kuò)容方法
private void grow(int minCapacity) { // size + 1
// overflow-conscious code 獲得舊的容量大小值
int oldCapacity = elementData.length;
// 新的容量 = 舊的容量 + (舊的容量 / 2) 也就是大概擴(kuò)容1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 如果新的容量比期望容量小赢笨,那么期望容量為新的容量
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity; // size + 1
// 如果新的容量超過最大范圍未蝌,則調(diào)用hugeCapacity方法去決定新的容量的大小
// private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
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); // size + 1
}
// 確保不小于0并且不超過最大容量
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
//Integer.MAX_VALUE - 8;
MAX_ARRAY_SIZE;
}
public static <T> T[] copyOf(T[] original, int newLength) { // size + 1
return (T[]) copyOf(original, newLength, original.getClass());
}
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);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
16 刪除的代碼因?yàn)椴簧婕暗綌U(kuò)容茧妒,所以比起add較為簡(jiǎn)單萧吠,首先會(huì)檢查數(shù)組是否下標(biāo)越界,然后會(huì)獲取指定位置的元素桐筏,接著進(jìn)行數(shù)據(jù)的搬移怎憋,將--size位置的元素置成null,讓GC進(jìn)行回收九昧。最后將目標(biāo)元素返回即可绊袋。
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 < 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;
}
private void fastRemove(int index) {
modCount++;
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
}
17 清空元素
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
18 將指定集合中的所有元素按照指定集合的迭代器返回它們的順序追加到此列表的末尾
public boolean addAll(Collection<? extends E> 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;
}
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;
}
19 刪除指定范圍的元素
protected void removeRange(int fromIndex, int toIndex) {
modCount++;
int numMoved = size - toIndex;
System.arraycopy(elementData, toIndex, elementData, fromIndex,
numMoved);
// clear to let GC do its work
int newSize = size - (toIndex-fromIndex);
for (int i = newSize; i < size; i++) {
elementData[i] = null;
}
size = newSize;
}
20 從該列表中刪除指定集合中包含的所有元素
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, false);
}
public static <T> T requireNonNull(T obj) {
if (obj == null)
throw new NullPointerException();
return obj;
}
private boolean batchRemove(Collection<?> c, boolean complement) {
final Object[] elementData = this.elementData;
int r = 0, w = 0;
boolean modified = false;
try {
for (; r < size; r++)
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
} finally {
// Preserve behavioral compatibility with AbstractCollection,
// even if c.contains() throws.
if (r != size) {
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
if (w != size) {
// clear to let GC do its work
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
}
}
return modified;
}
分析批量刪除方法都做了啥:
1.Objects.requireNonNull(c);判斷非空
2.return batchRemove(c, false);調(diào)用私有方法去刪除
3.來到 batchRemove(Collection<?> c, boolean complement)
說明兩個(gè)參數(shù):
Collection<?> c = c;要?jiǎng)h除的集合
boolean complement = false;
是作為elementData數(shù)組元素不在集合c的標(biāo)志。(即需要保留的數(shù)組元素)
當(dāng)complement為true時(shí)铸鹰,求list中與collection的并集癌别。
當(dāng)complement為false時(shí),求list與collection的差集蹋笼。
4.final Object[] elementData = this.elementData;被final修飾的變量展姐,不可變的是變量的引用,而不是變量的內(nèi)容
5.boolean modified = false;
6.遍歷數(shù)組elementData
if (c.contains(elementData[r]) == complement)
表示elementData[r]不在要?jiǎng)h除的集合內(nèi)剖毯,則把這些需要保留的元素elementData[r]圾笨,重置到數(shù)組本身(從0開始)elementData[w++]
7.接下來走finally方法
r != size;表示判斷r是否會(huì)等于size逊谋。如果說前面的代碼不停止的話擂达,那么最后r會(huì)將ArrayList里面的元素全部遍歷完畢,最后r的值會(huì)等于size的值胶滋,并且跳出前面那個(gè)for循環(huán)板鬓。
如果r不等于size:
System.arraycopy(elementData, r,elementData, w, size - r);
表示elementData從r索引開始,截取size-r個(gè)元素究恤,然后把截取的所有元素從w開始賦值到elementData
也就是說俭令,如果數(shù)組elementData沒有遍歷完就跳出了循環(huán),把剩下沒遍歷完的當(dāng)作不需要?jiǎng)h除的數(shù)組元素部宿,放到保留的數(shù)組元素的數(shù)組后面抄腔。
如果r等于size:
繼續(xù)判斷,如果w不等于size:
也就是說理张,原數(shù)組elementData已經(jīng)遍歷完赫蛇,w不等于size表示集合c存在要?jiǎng)h除的數(shù)組元素
則從w開始遍歷,即在w往后的元素都置空elementData[i] = null;讓GC去工作
8.modCount += size - w;
9.修改數(shù)組大醒那睢:size = w;
10.modified = true;
11.最后返回modified
- 小結(jié):
finally里面的代碼塊是暫存作用棍掐,為什么只有(r!=size)的時(shí)候才去執(zhí)行里面的代碼塊?明明try代碼塊里面的r最后肯定會(huì)是r=size才會(huì)跳出for循環(huán)的拷况。那么為什么后面也要設(shè)置一個(gè)(w!=size)的判定呢作煌?我們假設(shè)前面的直接走完了,并沒有走(r!=size)里面的代碼赚瘦,此時(shí)有兩種情況粟誓,
剛好w=size,沒有執(zhí)行(w!=size)里面的代碼起意,
此時(shí)modified=false鹰服,我們就知道,ArrayList里面的數(shù)據(jù)并沒有發(fā)生改動(dòng)。
另外一種情況悲酷,執(zhí)行了(w!=size)里面的代碼套菜,最后size=w。
什么情況會(huì)中斷for循環(huán)呢设易?
writeObject方法逗柴,傳遞的參數(shù)是一個(gè)ObjectOutputStream對(duì)象,這是一個(gè)寫入流顿肺,在注釋中也對(duì)這個(gè)方法進(jìn)行了解釋戏溺,將ArrayList的狀態(tài)保存進(jìn)流里面。
此時(shí)屠尊,我們多次看到過的modCount就起到作用了旷祸,先定義了一個(gè)expectedModCount用來保存它。然后執(zhí)行默認(rèn)的序列化操作讼昆,然后將大小寫入進(jìn)去托享,然后通過for循環(huán)把所有元素按照正確的順序?qū)懭脒M(jìn)去。
每當(dāng)ArrayList里面的內(nèi)容被改動(dòng)過的時(shí)候控淡,modCount都會(huì)增加嫌吠,if (modCount != expectedModCount)的判斷是為了確保,當(dāng)把ArrayList里面的數(shù)據(jù)寫入流里面的時(shí)候是最新數(shù)據(jù)掺炭。如果在寫入流的過程中辫诅,數(shù)據(jù)發(fā)生了變化,那么這個(gè)數(shù)據(jù)就不是最新的數(shù)據(jù)涧狮,那么就拋出錯(cuò)誤ConcurrentModificationException()炕矮。
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{
// Write out element count, and any hidden stuff
int expectedModCount = modCount;
s.defaultWriteObject();
// Write out size as capacity for behavioural compatibility with clone()
s.writeInt(size);
// Write out all elements in the proper order.
for (int i=0; i<size; i++) {
s.writeObject(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
21 保留集合的所有元素
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 < size; r++)
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
} finally {
// Preserve behavioral compatibility with AbstractCollection,
// even if c.contains() throws.
if (r != size) {
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
if (w != size) {
// clear to let GC do its work
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
}
}
return modified;
}
分析它都做了啥:
1.調(diào)用的方法跟removeAll調(diào)用的一樣:batchRemove
2.但是,傳入的一個(gè)參數(shù)不一樣者冤,就是complement這里為true肤视;表示該集合c的元素需要保留
3.那么,遍歷數(shù)組元素涉枫,if (c.contains(elementData[r]) == complement)表示該元素在集合內(nèi):
則把這些需要保留的元素elementData[r]邢滑,重置到數(shù)組本身(從0開始)elementData[w++]
【因此可以分析得出,無論是removeAll還是retainAll愿汰,都是通過控制complement為true或者false來篩選出需要保留的元素重置到elementData[w++]】
4.接下來的步驟和removeAll的分析一致困后,就不做贅述