[直擊native] 單步調試ArrayList 源碼 一
源碼版本 jdk 1.8
源碼調式過程中需要不停的進入函數內部,函數內部的函數 根據調試順序在當前函數的後面
如果調試函數調用的是完全相同函數 則不再贅述
詳細調試從ArrayList 對象初始化開始
文章目錄
類繼承關係
主要public 方法
基礎方法解讀
測試代碼
@Test
public void createList() {
List<String> arr1 = new ArrayList<>();
arr1.add("12222222");
List<String> arr2 = new ArrayList<>(10);
List<String> arr3 = new ArrayList<>(arr1);
}
@Test
public void arrayConvertTest() {
class MyArr extends ArrayList {
@Override
public String[] toArray() {
return new String[]{"123"};
}
}
List<String> arr = new MyArr();
Object[] objects = arr.toArray();
objects[0] = new Object();
}
@Test
public void arrayCreateCompareTest() throws ClassNotFoundException {
//創建空數組
Object[] numbers = (Object[]) new Object[10];
// stringClass componentType 等價
Class<String> stringClass = String.class;
Class<?> componentType = String[].class.getComponentType();
//動態創建空數組
String[] strings = (String[]) Array.newInstance(componentType, 10);
for (Object number : numbers) {
System.out.println(number);
}
for (String string : strings) {
System.out.println(string);
}
//全部是空數組
}
@Test
public void classCompareTest() {
Class newType = Object[].class;
Class newType1 = String[].class;
//true
System.out.println((newType == Object[].class));
//true
System.out.println(((Object) newType == (Object) Object[].class));
//false
System.out.println((newType1 == Object[].class));
//false
System.out.println(((Object) newType1 == (Object) Object[].class));
}
//等價
public static <T, U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
//這一行強轉可以不需要 ,在下面 測試代碼 jdk 8 運行下與不強轉等價
//https://stackoverflow.com/questions/29494800/do-not-understand-the-source-code-of-arrays-copyof
//根據上面介紹 這個強轉可能是爲了更好的兼容低版本 和性能加的 強轉
Object newTypeObject = (Object) newType;
T[] copy = null;
if (newTypeObject == (Object) Object[].class) {
//如果是已知的object 數組 直接new
copy = (T[]) new Object[newLength];
} else {
//如果不是已知的則 使用動態的方法 創建對應類型空數組
//其引用的 是底層的native 方法
copy = (T[]) Array.newInstance(newType.getComponentType(), newLength);
}
//獲得空數組之後 從original 的0 位置開始複製 到copy 取最小的一個值(代表有數據,空值不用賦值了)
//這裏 同樣是調用 底層的 native 方法
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
//最後返回複製得到的新數組 , 可以知道是 大容量 同樣數據的新數組
return copy;
}
@Test
public void addElementToArrayList(){
// 添加元素 變化不同情況 在 分析構造器時提到 elementData 在第一次添加時會擴展到默認大小
// 所以用兩個構造器分別添加元素測試
// 查看添加過程代碼
List<String> arr1 = new ArrayList<>();
arr1.add("12222222");
arr1.add("12222222");
arr1.add(0,"12222222");
// IndexOutOfBoundsException
//arr1.add(7,"12222222");
List<String> arr2 = new ArrayList<>();
arr2.addAll(arr1);
arr2.addAll(2,arr1);
}
@Test
public void arraycopyTest(){
Integer[] src = new Integer[5];
src[0] = 20;
src[1] = 21;
src[2] = 22;
src[3] = 23;
Integer[] dest = new Integer[5];
System.arraycopy(src,0,dest,1,3);
for (Integer integer : src){
System.out.println(integer);
}
System.out.println();
for (Integer integer : dest){
System.out.println(integer);
}
/***
* 20
* 21
* 22
* 23
* null
*
* null
* 20
* 21
* 22
* null
*/
}
@Test
public void setElementTest(){
List<String> arr = new ArrayList<>();
/*
arr.add("1");
arr.add("2");
arr.add("3");
*/
String set = arr.set(2, "6");
System.out.println(set);
}
@Test
public void removeTest() {
List<String> arr = new ArrayList<>();
String string = new String("123");
arr.add(string);
arr.add(string);
// arr.remove(1);
arr.removeAll(arr);
for (String s:arr ) {
System.out.println(s);
}
arr.clear();
}
@Test
public void getTest(){
List<String> arr = new ArrayList<>();
String string = "123";
arr.add(string);
arr.add(string);
String s = arr.get(0);
int indexOf = arr.indexOf(string);
int lastIndexOf = arr.lastIndexOf(string);
boolean contains = arr.contains(arr);
boolean containsAll = arr.containsAll(arr);
}
ArrayList 對象初始化
空參構造
//空參 默認賦值 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 給elementData 緩衝取元素數組
/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//下面是幾個源碼中常用內部變量
/**
* 默認容量 10
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* 空元素 對象數組
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* Shared empty array instance used for default sized empty instances. We
* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
* first element is added.
* 空元素 對象數組 這個是默認的 與上面的空數組 區分
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
* 這個是ArrayList 底層的存儲結構 容量爲 數組長度
空數組 默認等於DEFAULTCAPACITY_EMPTY_ELEMENTDATA
當第一個元素添加的時候 拓展到默認容量
*/
transient Object[] elementData; // non-private to simplify nested class access
註釋寫道: 初始化默認elementData 爲 空數組 當第一個元素添加 將擴容到默認容量
帶參構造函數
其中指定容量構造方法是 推薦使用的
// 這個可以指定一個集合傳入,必須是含有e的子類的 Collection的子類
/**
* Constructs a list containing the elements of the specified
* collection, in the order they are returned by the collection's
* iterator.
*
* @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) {
// 列表轉換成 數組 調用的爲 Collection<? extends E> 集合的 方法
// 也正是因爲這裏的 toArray 具體實現不可空 所以下面代碼產生了 bug 6260652
// 判斷也正是爲了修復這個bug
elementData = c.toArray();
// 此處:調用 返回的是 對象數組或對象子類數組 直接賦值給這個arraylist 緩衝區數組
if ((size = elementData.length) != 0) {
//先將size 設置爲 緩衝數組長度 並且如果不爲零
// c.toArray might (incorrectly) not return Object[] (see 6260652)
//此處判斷是否爲對象數組
//比如:
//class MyArr extends ArrayList{
// @Override
// public String[] toArray(){
// return new String[]{"cc190911"};
// }
//}
//List<String> arr = new MyArr();
//Object[] objects = arr.toArray();
//objects[0] = new Object();
//java.lang.ArrayStoreException: java.lang.Object
//如果不爲對象數組則 使用底層的Arrays 工具類重新賦值
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {//爲零則將空數組賦值給緩衝數組
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
//指定容量
/**
* Constructs an empty list with the specified initial capacity.
*
* @param initialCapacity the initial capacity of the list
* @throws IllegalArgumentException if the specified initial capacity
* is negative
*/
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);
}
}
Arrays 底層方法
此方法是後續實現數組動態擴容的重要方法
/**
複製指定的數組,截斷或用空填充(如有必要),以便複製具有指定的長度。
對於在原始數組和副本中都有效的所有索引,這兩個數組將包含相同的值。
對於副本中有效但不是原始索引的任何索引,副本將包含<tt>null</tt>。
當且僅當指定的長度大於原始數組的長度時,才存在此類索引。
得到的數組是類<tt>newType</tt>。
* 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;
}
//等價
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
//這一行強轉可以不需要 ,在下面 測試代碼 jdk 8 運行下與不強轉等價
//https://stackoverflow.com/questions/29494800/do-not-understand-the-source-code-of-arrays-copyof
//根據上面介紹 這個強轉可能是爲了更好的兼容低版本 和性能加的 強轉
Object newTypeObject = (Object)newType;
T[] copy = null;
if (newTypeObject== (Object)Object[].class){
//如果是已知的object 數組 直接new
copy = (T[]) new Object[newLength] ;
}else{
//如果不是已知的則 使用動態的方法 創建對應類型空數組
//其引用的 是底層的native 方法
copy = (T[]) Array.newInstance(newType.getComponentType(), newLength);
}
//獲得空數組之後 從original 的0 位置開始複製 到copy 取最小的一個值(代表有數據,空值不用賦值了)
//這裏 同樣是調用 底層的 native 方法
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
//最後返回複製得到的新數組 , 可以知道是 大容量 同樣數據的新數組
return copy;
}
@Test
public void classCompareTest(){
Class newType = Object[].class ;
Class newType1 = String[].class ;
//true
System.out.println((newType==Object[].class));
//true
System.out.println(((Object) newType==(Object) Object[].class));
//false
System.out.println((newType1==Object[].class));
//false
System.out.println(((Object) newType1==(Object) Object[].class));
}
ArrayList添加元素
以下方式添加方法
- 添加元素 到最後位置
- 添加元素 到固定位置
- 添加集合類全部元素到最後位置
- 添加集合類全部元素到指定位置
[外鏈圖片轉存失敗(img-J3ZLEjly-1568275586616)(1568100863519.png)]
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
public boolean add(E e) {
// 確保容量範圍內
ensureCapacityInternal(size + 1); // Increments modCount!!
// 把對象賦值到 當前數組的位置 並將size + 1
elementData[size++] = e;
return true;
}
//確保容量在範圍內
private void ensureCapacityInternal(int minCapacity) {
// 此處 可以看到 如果elementData是DEFAULTCAPACITY_EMPTY_ELEMENTDATA(默認空)
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
// 擴容到DEFAULT_CAPACITY minCapacity 較大的一個
// 直接空參初始化 size = 0 minCapacity = 1
// 所以直接是最小容量爲DEFAULT_CAPACITY = 10
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
// 確保顯性容量
private void ensureExplicitCapacity(int minCapacity) {
// 修改次數加一
// 在 java.util.AbstractList
// protected transient int modCount = 0;
// 這是個很重要的變量 , 後面講到
modCount++;
// 自動增長 , 如果傳遞的minCapacity需要最小的容量 比當前緩衝的對象數組大
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
/**
擴容函數
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
private void grow(int minCapacity) {
// overflow-conscious code
// 未擴容 實際數組長度
int oldCapacity = elementData.length;
// 新容量 = 長度 + 長度右移1位(長度一半)
// 這裏也是爲什麼 新的容量是 之前的1.5倍
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:
// 這樣處理 可以使 最小值 儘可能接近 大小 這樣可以節省內存空間
// 還是調用數組動態賦值函數如下 , 其底層還是調用
// copyOf(original, newLength, original.getClass())
elementData = Arrays.copyOf(elementData, newCapacity);
}
// 大容量 函數計算
private static int hugeCapacity(int minCapacity) {
// 如果小於零 溢出錯誤
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
// 如果 最小容量大於最大數組大小 則返回 整型最大值
// 否則 還是 返回 最大最大數組大小
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
// 最大數組大小 爲 整形最大值 - 8
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
// Integer.MAX_VALUE
@Native public static final int MAX_VALUE = 0x7fffffff;
//Arrays 的函數
public static <T> T[] copyOf(T[] original, int newLength) {
return (T[]) copyOf(original, newLength, original.getClass());
}
/**
將指定元素插入到列表中的指定位置。
將當前位於該位置的元素(如果有的話)和隨後的任何元素向右移動(將一個元素添加到它們的索引中)。
* Inserts the specified element at the specified position in this
* list. Shifts the element currently at that position (if any) and
* any subsequent elements to the right (adds one to their indices).
*
* @param index index at which the specified element is to be inserted
* @param element element to be inserted
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public void add(int index, E element) {
// 檢查 是否越界
rangeCheckForAdd(index);
// 大小加一 後 確保在容量內
ensureCapacityInternal(size + 1); // Increments modCount!!
// 調用底層函數 複製
// 從當前長度 size - index 的 elementData的index 複製到 elementData的index + 1
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
// 將index 位置賦值爲當前元素
elementData[index] = element;
// 大小加一
size++;
}
/**
檢查索引異常 如果 當前檢索大於 size 或者 小於 0 拋出數組越界異常
可見 不能在當前list的size之後位置使用add 方法 添加 元素
* A version of rangeCheck used by add and addAll.
*/
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
// 這個打印 當前 越界信息
private String outOfBoundsMsg(int index) {
return "Index: "+index+", Size: "+size;
}
/**
按指定集合的迭代器返回的順序,將指定集合中的所有元素追加到此列表的末尾。
如果在操作進行期間修改了指定的集合,則此操作的行爲未定義。
(這意味着,如果指定的集合是這個列表,並且這個列表不是空的,則此調用的行爲是未定義的。)
* Appends all of the elements in the specified collection to the end of
* this list, in the order that they are returned by the
* specified collection's Iterator. The behavior of this operation is
* undefined if the specified collection is modified while the operation
* is in progress. (This implies that the behavior of this call is
* undefined if the specified collection is this list, and this
* list is nonempty.)
*
* @param c collection containing elements to be added to this list
* @return <tt>true</tt> if this list changed as a result of the call
* @throws NullPointerException if the specified collection is null
*/
public boolean addAll(Collection<? extends E> c) {
// 返回 集合類中的對象數組 , 這裏測試c用的 ArrayList
// 下面分析下 ArrayList 的toArray
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;
}
/**
返回的是第一個到最後一個的元素數組對象
* Returns an array containing all of the elements in this list
* in proper sequence (from first to last element).
* 這個返回的數組是安全的 沒有任何引用 也就是重新創建的一個 新的數組
* <p>The returned array will be "safe" in that no references to it are
* maintained by this list. (In other words, this method must allocate
* a new array). The caller is thus free to modify the returned array.
* 這個方法充當 基礎數組和基本集合類的轉換橋樑
* <p>This method acts as bridge between array-based and collection-based
* APIs.
*
* @return an array containing all of the elements in this list in
* proper sequence
*/
public Object[] toArray() {
// 可以看到調用的是上面 Arrays.copyOf
// 因此的到的是一個全新的數組副本
return Arrays.copyOf(elementData, size);
}
/**
從指定位置開始,將指定集合中的所有元素插入此列表。
將當前位於該位置的元素(如果有的話)和隨後的任何元素移動到右邊(增加它們的索引)。
新元素將按指定集合的迭代器返回它們的順序出現在列表中。
* Inserts all of the elements in the specified collection into this
* list, starting at the specified position. Shifts the element
* currently at that position (if any) and any subsequent elements to
* the right (increases their indices). The new elements will appear
* in the list in the order that they are returned by the
* specified collection's iterator.
*
* @param index index at which to insert the first element from the
* specified collection
* @param c collection containing elements to be added to this list
* @return <tt>true</tt> if this list changed as a result of the call
* @throws IndexOutOfBoundsException {@inheritDoc}
* @throws NullPointerException if the specified collection is null
*/
public boolean addAll(int index, Collection<? extends E> c) {
// 同樣的 索引檢測越界
rangeCheckForAdd(index);
// 轉換成對象數組
Object[] a = c.toArray();
// 得到數組實際長度
int numNew = a.length;
// 確保 容量足夠
// 這裏 modCount 只會加1
ensureCapacityInternal(size + numNew); // Increments modCount
// 移動量 = 當前大小減 索引位置
int numMoved = size - index;
// 如果大於零 則特殊處理
// 先將 elementData index 後的元素往後移動 numMoved
// 這裏 不會出現 小於零的情況 因爲前面有越界檢測
if (numMoved > 0)
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
// 移動之後就相當於等於零
// 將需要添加的數組numNew長度賦值到 elementData 的 index位置和之後
System.arraycopy(a, 0, elementData, index, numNew);
// 當前大小增大 numNew
size += numNew;
// 長度不爲0 則成功 其實和之前直接返回 true 差不多
// 不過也需要注意一下 如果長度爲零 返回的是false
return numNew != 0;
}
ArrayList 修改元素
修改元素使用的是set(int index, E element) , 與添加不同的是它只有一個函數
上面有個問題是,如果添加元素在當前數組之外 , 容量之內 是不能添加的
那麼,set 是否可以呢?
/**
用指定的元素替換列表中指定位置的元素。
* Replaces the element at the specified position in this list with
* the specified element.
*
* @param index index of the element to replace 要替換的元素的索引
* @param element element to be stored at the specified position 要存儲在指定位置的元素
* @return the element previously at the specified position 先前位於指定位置的元素
* @throws IndexOutOfBoundsException {@inheritDoc} 數組越界異常
*/
public E set(int index, E element) {
// 看到這個check 函數 應該就猜到 可能有越界檢測
// 所以函數在數組沒有任何值的時候是不能使用set 方法的
rangeCheck(index);
// 獲取實際元素類型的對象
E oldValue = elementData(index);
// 將元素存入
elementData[index] = element;
// 返回之前在這個位置的元素
return oldValue;
// 函數結束 沒有修改 modCount 值
}
/**
檢查給定索引是否在範圍內。
如果沒有,則拋出適當的運行時異常。
這個方法不檢查索引是否爲負數:它總是在數組訪問之前使用,如果索引爲負數,數組訪問將拋出一個ArrayIndexOutOfBoundsException。
* Checks if the given index is in range. If not, throws an appropriate
* runtime exception. This method does *not* check if the index is
* negative: It is always used immediately prior to an array access,
* which throws an ArrayIndexOutOfBoundsException if index is negative.
*/
private void rangeCheck(int index) {
// 這裏只判斷了 是否 大於數組大小
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
// Positional Access Operations
@SuppressWarnings("unchecked")
E elementData(int index) {
// 裏面就是常見的 下標獲取
// 只是添加了類型強轉 , 將之前存入的類型 還原
return (E) elementData[index];
}
ArrayList 刪除元素
刪除元素有三個方法
public E remove(int index) ; 指定索引刪除,會返回指定位置的元素
public boolean remove(Object o) ;//指定元素刪除 ,返回是否成功刪除
public boolean removeAll(Collection<?> c) ;//指定集合刪除,返回是否成功刪除
public void clear() ; // 刪除全部
/**
移除列表中指定位置的元素。將任何後續元素向左移動(從它們的索引中減去一個)。
* Removes the element at the specified position in this list.
* Shifts any subsequent elements to the left (subtracts one from their
* indices).
*
* @param index the index of the element to be removed
* @return the element that was removed from the list
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E remove(int index) {
// 同樣是越界檢測 只檢測是否在大小內
rangeCheck(index);
// 修改次數加一
modCount++;
// 同樣是獲取位置的對應類型的元素
E oldValue = elementData(index);
// 判斷元素移動量 , 此處邏輯和指定位置添加集合的邏輯相同
int numMoved = size - index - 1;
// 如果大於0 就需要往左移numMoved 位
// 所以 如果這個相對位移越大 消耗的時間也是越多的
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
// 因爲左移之後 最右邊和最右邊的左邊一位是相同
// 所以先將元素置null 然後將大小減一
// 置null 是爲了jvm gc 這裏涉及到jvm 的gc 工作原理
// 可以理解爲如果對象有引用的話 jvm內存管理認爲這些對象還在使用 所以不會那麼快釋放(實際已經是無用對象)
// 所以在集合類使用過程中 儘量使用之後也將其置null
// 如: list = null;
elementData[--size] = null; // clear to let GC do its work
// 返回 之前位置對象
return oldValue;
}
/**
從列表中刪除指定元素的第一個出現項(如果存在)。
如果列表不包含該元素,它將保持不變。
更正式地說,刪除索引最低的元素<tt>i</tt>,
使<tt>(o==null ? get(i)==null: o. = (get(i)) </tt>(如果存在這樣一個元素)
。如果該列表包含指定的元素,則返回<tt>true</tt>(如果該列表由於調用而更改,則返回相同的結果)。
* Removes the first occurrence of the specified element from this list,
* if it is present. If the list does not contain the element, it is
* unchanged. More formally, removes the element with the lowest index
* <tt>i</tt> such that
* <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>
* (if such an element exists). Returns <tt>true</tt> if this list
* contained the specified element (or equivalently, if this list
* changed as a result of the call).
*
* @param o element to be removed from this list, if present
* @return <tt>true</tt> if this list contained the specified element
*/
public boolean remove(Object o) {
// 判斷是否爲空 , 所以這個也是可以保存null 的
// 同時也爲了避免後面equals npe
if (o == null) {
// 遍歷
for (int index = 0; index < size; index++)
// 如果有一個爲空
if (elementData[index] == null) {
// 調用私有方法 移除
fastRemove(index);
// 直接返回true 所以 移除對象的方法 只會刪除最左邊第一個匹配的對象
return true;
}
} else {
for (int index = 0; index < size; index++)
// 唯一不同的是調用的是存入對象的equals 方法
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
// 最後如果沒有找到 則返回false
return false;
}
/*
私有的remove方法,該方法跳過邊界檢查,並且不返回已刪除的值。
* Private remove method that skips bounds checking and does not
* return the value removed.
*/
private void fastRemove(int index) {
// 進來發現 是不是似曾相識
// 和上面的 set add集合 是相似的代碼結構
// 注意的是這個方法 並沒有邊界檢查 並且也不返回刪除的值
// 那麼 爲什麼上面的set add 集合不調用這個函數呢?
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
}
/**
從該列表中刪除指定集合中包含的所有元素。
* Removes from this list all of its elements that are contained in the
* specified collection.
*
* @param c collection containing elements to be removed from this list
* @return {@code true} if this list changed as a result of the call
* @throws ClassCastException if the class of an element of this list
* is incompatible with the specified collection
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if this list contains a null element and the
* specified collection does not permit null elements
* (<a href="Collection.html#optional-restrictions">optional</a>),
* or if the specified collection is null
* @see Collection#contains(Object)
*/
public boolean removeAll(Collection<?> c) {
// 一種規範性的寫法:不爲空判斷 , jdk7 推出的 對象工具類
Objects.requireNonNull(c);
// 批量移除
// 注意這個false
return batchRemove(c, false);
}
/**
* This class consists of {@code static} utility methods for operating
* on objects. These utilities include {@code null}-safe or {@code
* null}-tolerant methods for computing the hash code of an object,
* returning a string for an object, and comparing two objects.
*
* @since 1.7
*/
public final class Objects {
/**
* Checks that the specified object reference is not {@code null}. This
* method is designed primarily for doing parameter validation in methods
* and constructors, as demonstrated below:
* <blockquote><pre>
* public Foo(Bar bar) {
* this.bar = Objects.requireNonNull(bar);
* }
* </pre></blockquote>
*
* @param obj the object reference to check for nullity
* @param <T> the type of the reference
* @return {@code obj} if not {@code null}
* @throws NullPointerException if {@code obj} is {@code null}
*/
public static <T> T requireNonNull(T obj) {
if (obj == null)
throw new NullPointerException();
return obj;
}
}
private boolean batchRemove(Collection<?> c, boolean complement) {
// 將 elementData 對象數組 引用到 局部變量
final Object[] elementData = this.elementData;
//新建的兩個變量,那源碼中忽然看到這麼奇怪命名的變量怎麼辦?接着往下分析.
int r = 0, w = 0;
// 是否修改標識 用於後面返回是否成功移除
boolean modified = false;
try {
// 循環遍歷對象數組 , 可以知道 r 是用於遍歷對象數組 單獨拿出來是因爲後面也要用到
for (; r < size; r++)
// 判斷 當前對象是否在傳入集合對象中
// 返回 true false 再與 complement 比較
// 如果 complement = false 當前對象不在集合類中
if (c.contains(elementData[r]) == complement)
// 則將當前對象賦值給elementData
// 並且是放在w下標位置 w 是新的局部變量中elementData 的下標指向
// 每修改一個就會往右移
elementData[w++] = elementData[r];
} finally {
// 即使c.contains()拋出異常,也要保持與AbstractCollection的行爲兼容性。
// Preserve behavioral compatibility with AbstractCollection,
// even if c.contains() throws.
// r不等於size,爲什麼會不等,那麼肯定是拋出了異常
// 可能 ClassCastException NullPointerException等
if (r != size) {
// 將後續沒有對比的 都加入到elementData的w位置之右
System.arraycopy(elementData, r,
elementData, w,
size - r);
// w 加上後續沒有比較的長度
w += size - r;
}
// 如果w 不等於 size ,說明在對象數組中有剔除元素
if (w != size) {
// 同樣將剩餘元素後面的數組對象 置空 help gc
// clear to let GC do its work
for (int i = w; i < size; i++)
elementData[i] = null;
// 增加修改次數
// 這裏剔除多少元素 就修改多少次
modCount += size - w;
// 將實際大小修改爲 size
size = w;
// 修改成功
modified = true;
}
}
// 返回
return modified;
/****
*
函數結束: 所以將this.elementData 引用賦值給局部變量 ,實際上還是操作 this.elementData
r 是爲了遍歷對象 和判斷是否遍歷成功
遍歷之後 根據 complement 判斷是否包含 , 所以這裏傳入的是false 將不包含的留下
同理,也可以傳入true 將包含的留下
w 是留下的新數組長度
modCount 修改次數爲 剔除元素個數
**/
}
/**
移除全部 函數
* Removes all of the elements from this list. The list will
* be empty after this call returns.
*/
public void clear() {
// 修改次數加一
modCount++;
// 將數組 全部置null
// 這裏只是數組不持有對象的引用 並改變實際對象的值
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
// 大小爲 0
size = 0;
}
ArrayList 查找元素
public E get(int index) ;
public int indexOf(Object o) ;
public int lastIndexOf(Object o) ;
public boolean contains(Object o);
public boolean containsAll(Collection<?> c) ;
public Iterator iterator() ;
/**
* Returns the element at the specified position in this list.
*
* @param index index of the element to return
* @return the element at the specified position in this list
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E get(int index) {
// 越界檢測
rangeCheck(index);
// 返回元素 看到這裏就會發現 後面的函數在之前基本上都講過了
return elementData(index);
}
/**
這個函數上面也是講過的
* Returns the index of the first occurrence of the specified element
* in this list, or -1 if this list does not contain the element.
* More formally, returns the lowest index <tt>i</tt> such that
* <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>,
* or -1 if there is no such index.
*/
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;
}
/**
* Returns the index of the last occurrence of the specified element
* in this list, or -1 if this list does not contain the element.
* More formally, returns the highest index <tt>i</tt> such that
* <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>,
* or -1 if there is no such index.
*/
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;
}
/**
* Returns <tt>true</tt> if this list contains the specified element.
* More formally, returns <tt>true</tt> if and only if this list contains
* at least one element <tt>e</tt> such that
* <tt>(o==null ? e==null : o.equals(e))</tt>.
*
* @param o element whose presence in this list is to be tested
* @return <tt>true</tt> if this list contains the specified element
*/
public boolean contains(Object o) {
// 還是調用上面的函數 如果大於等於0 就找到了
// 如果-1 就沒有找到就是false
return indexOf(o) >= 0;
}
/**
* {@inheritDoc}
*
* <p>This implementation iterates over the specified collection,
* checking each element returned by the iterator in turn to see
* if it's contained in this collection. If all elements are so
* contained <tt>true</tt> is returned, otherwise <tt>false</tt>.
*
* @throws ClassCastException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
* @see #contains(Object)
*/
public boolean containsAll(Collection<?> c) {
// 直接循環集合c
for (Object e : c)
// 判斷有不包含 直接返回false
if (!contains(e))
return false;
// 返回全部包含
return true;
}
* <tt>(o==null ? e==null : o.equals(e))</tt>.
*
* @param o element whose presence in this list is to be tested
* @return <tt>true</tt> if this list contains the specified element
*/
public boolean contains(Object o) {
// 還是調用上面的函數 如果大於等於0 就找到了
// 如果-1 就沒有找到就是false
return indexOf(o) >= 0;
}
/**
* {@inheritDoc}
*
* <p>This implementation iterates over the specified collection,
* checking each element returned by the iterator in turn to see
* if it's contained in this collection. If all elements are so
* contained <tt>true</tt> is returned, otherwise <tt>false</tt>.
*
* @throws ClassCastException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
* @see #contains(Object)
*/
public boolean containsAll(Collection<?> c) {
// 直接循環集合c
for (Object e : c)
// 判斷有不包含 直接返回false
if (!contains(e))
return false;
// 返回全部包含
return true;
}