這篇文章來自個人博客數組
上一篇文章分析了ArrayList的構造器和基本操做的源碼,這一次來說一講ArrayList經常使用方法剩下的源碼(迭代器的留到下次說)bash
主要內容ui
- 容器容量
- 查找特定元素,包含特定元素,克隆之類的基本用法
開始正題以前,須要先分清楚兩個概念,容器的capacity和size,capacity指的是容器的容量,最多能裝多少,而size指的是當前容器中元素的數量this
接下來直奔正題spa
public void ensureCapacity(int minCapacity) {
//若是初始時有設定容量,就按設定的來,沒有就按默認的來
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
// any size if not default element table
// larger than default for default empty table. It is already
// supposed to be at default size.
//: DEFAULT_CAPACITY;
//若是給定的比上述的要大,就按給定的來
if (minCapacity > minExpand) {
//這個方法在下面
ensureExplicitCapacity(minCapacity);
}
}
//私有方法,用來肯定容量
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
//增長容量
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
複製代碼
/**
* The maximum size of array to allocate.
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
*/
//註解說是由於有的虛擬機要保留數組的前幾位,這個常量的值爲2147483639
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
* 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;
//右移一位,至關於除以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) {
//溢出
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
複製代碼
若是發現容量多了不少,有點浪費,就能夠調整容器大小到當前大小3d
public void trimToSize() {
modCount++;
//若是確實有多餘部分
if (size < elementData.length) {
//若是size不爲0,則按照size複製數組
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
複製代碼
public int size() {
return size;
}
複製代碼
public boolean isEmpty() {
return size == 0;
}
複製代碼
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;
}
複製代碼
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
複製代碼
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;
}
複製代碼
須要先說明一個概念:淺拷貝和深拷貝指針
淺拷貝,兩個ArrayList在內存中的地址是不同的,可是他們中的元素都指向同一個地址,手繪一個圖解釋一下:code
深拷貝,不只ArrayList在內存中的地址是不同的,他們中的元素也不指向同一個地址,也手繪個圖解釋一下:cdn
深拷貝可能好理解一些,就是全部的東西都拷貝一下,徹底分離的狀態blog
ArrayList的**clone()**方法是淺拷貝,咱們來作個實驗:
public class Test {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(String.valueOf(i));
}
//強制類型轉換
ArrayList<String> list1 = (ArrayList<String>)list.clone();
System.out.println("list" + list);
System.out.println("list1" + list1);
System.out.println("兩者對於某個特定元素是否相同 " + (list.get(1) == list1.get(1)));
System.out.println("兩者的地址是否相同 " + (list == list1));
}
}
複製代碼
ArrayList是一個列表,和數組不同,不能直接用下標訪問元素,轉換爲數組就能夠了,轉換爲數組有兩種方法:
//按照列表中元素的順序,將列表中元素複製到一個數組
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
複製代碼
這一種方法可能會拋出數組存儲異常和空指針異常,這裏講述兩種狀況(數組大小小於列表元素數量):
傳入的數組類型和列表元素相同或者是列表元素的基類型,直接將列表中元素賦值在數組內
傳入的數組類型不是列表元素的基類型,拋出數組存儲異常
public <T> T[] toArray(T[] a) {
//傳入的數組大小更小
if (a.length < size)
//建立一個數組,類型是數組a的運行時類型
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
複製代碼
傳入的數組 s 是Object類型的,符合上述的狀況一:
接下來是狀況二:
這樣子,ArrayList經常使用的方法就講完了,下一篇就會是與ArrayList有關的迭代器的使用了