java集合Collection
一些實用的基本的東西,不會去分析UML圖。
下面列出一些主要包含關係,淡然集合還有queue隊列但不在此處研究
List:可以有重複元素的集合、有序(存入和取出的順序不一定相同)
- ArrayList:非線程安全、底層是數組的實現,所以方便查詢,增刪不方便
- LinkedList:非線程安全、底層是鏈表實現,所以增刪方便,查詢不方便
- Vector:幾乎和ArrayList一樣只不過是用來synchronized加鎖,所以效率低一點,但是線程安全
Set:不可以有重複元素的集合、無序
- HashSet:線程不安全,存取速度快。底層是以哈希表實現的。
- TreeSet:線程不安全,查詢速度快。底層是以紅黑樹實現的。
- LinkedHashSet:有序,線程不安全,內部是以LinkHashMap實現的。
ArrayList
基於jdk 1.8
常量
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
private static final long serialVersionUID = 8683452581122892189L;
//默認的大小
private static final int DEFAULT_CAPACITY = 10;
//空對象
private static final Object[] EMPTY_ELEMENTDATA = {};
//空對象使用默認構造器時使用
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//數據存放數組,不參與序列化
transient Object[] elementData; // non-private to simplify nested class access
//長度
private int size;
//最大的大小
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
構造函數
//帶初始大小的構造函數
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;
}
public ArrayList(Collection<? extends E> c) {
//將collection對象轉換成數組,然後將數組的地址的賦給elementData。
elementData = c.toArray();
//更新size的值,同時判斷size的大小,如果是size等於0,直接將空對象賦給elementData
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;
}
}
add
public boolean add(E e) {
//這個方法是確保有位子可以放數據
ensureCapacityInternal(size + 1); // Increments modCount!!
//存放數據,並讓size自增
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
//如果默認的空對象,則對比默認容量和給定容量,取大
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
//判斷是否需要擴容
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
//這個是記錄操作次數的,不用管
modCount++;
//判斷給定長度是否大於數據長度,如果給定容量大說明放不下,需要擴容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
//擴容大小爲本身大小+本身大小/2,也就是1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
//判斷是否滿足現在的需求,否就直接擴容到現在需要的大小
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//判斷是否超出界限
if (newCapacity - MAX_ARRAY_SIZE > 0)
//這個方法裏面判斷了是否大於int最大上限,如果是就取上限否則就是MAX_ARRAY_SIZE
newCapacity = hugeCapacity(minCapacity);
//複製擴容
elementData = Arrays.copyOf(elementData, newCapacity);
}
public void add(int index, E element) {
//這個方法只是判斷是否越界,越界就拋出異常
rangeCheckForAdd(index);
//和add方法一樣
ensureCapacityInternal(size + 1); // Increments modCount!!
//直接複製index後面的數據給elementData,並在index存入數據
//相當於index後面的數據整體後移
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
get
這個太簡單了只是驗證超界然後從數組裏取數據
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
set
簡單的判斷越界,取原數據存新數據並返回原數據
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
remove
remove有兩種一個根據位置 一種根據數據
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;
}
根據對象刪除分成兩種情況一種是循環判斷刪除用於刪除null,另外一個就是循環equals判斷刪除,真實的刪除方法是fastRemove(int index);
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
}
clear
循環清空數據,size變爲0,注意容量不變
public void clear() {
modCount++;
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
addAll
兩個方法和add方法差不多只不過從添加一個數據變成了,添加多個
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;
}
contains
本質就是循環遍歷數組看看元素是否存在
public boolean contains(Object o) {
return indexOf(o) >= 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;
}
trimToSize
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
//這個方法主要就是去除size後面的容量
: Arrays.copyOf(elementData, size);
}
}
到這裏ArrayList算是差不多瞭解析了,還有一些內部類,自己的序列化方式等沒解析,也不在這裏講了,有興趣的小夥伴可以自己去看看。
知道上面一些基本就夠用了
小結
- ArrayList基於數組方式實現,擴容上限int的最大值
- 添加元素時可能要擴容,刪除元素時不會減少容量(若希望減少容量,trimToSize()),刪除元素時,將刪除掉的位置元素置爲null,下次gc就會回收這些元素所佔的內存空間。
- 線程不安全
- add(int index, E element):添加元素到數組中指定位置的時候,需要將該位置及其後邊所有的元素都整塊向後複製一位,所以效率比add低
- remove(Object o)需要遍歷數組,效率比不需要遍歷數組的remove(int index)低
- 每次擴容默認是1.5倍,如果還不夠就是給定的長度
- 默認大小是10