jdk1.6ArrayList底層實現
轉載自五月的倉頡部落格地址:http://www.cnblogs.com/xrq730/p/4989451.html
對於集合需要關注四點
1. 是否允許為空
2. 是否允許重複
3. 是否有序(取出元素的順序是否和插入的順序一致)
4. 是否是執行緒安全的
ArrayList
Arraylist就是一個以陣列實現的集合,它的成員變數構成:
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer.
*/
private transient Object[] elementData;
/**
* The size of the ArrayList (the number of elements it contains).
*
* @serial
*/
private int size;
elementData: 就是ArrayList集合底層的陣列
size:ArrayList中元素的個數,size按照呼叫add,remove的次數自增或者自減,就是add null也會去自增1
ArrayList的特性
關注點 | 結論 |
ArrayList是否允許為空 | 允許 |
ArrayList是否允許重複 | 允許 |
ArrayList是否有序 | 有序 |
是否執行緒安全 | 執行緒不安全 |
建構函式
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
}
public ArrayList() {
this(10);
}
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
size = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
例項化集合的時候,可以指定集合的容量(就是底層陣列的容量),不指定容量大小預設size=10
也能傳入一個集合作為初始化引數,此時,會將集合轉為陣列,並通過Arrays.copyOf生成新的陣列,將引用指向這個新的陣列
新增元素和擴容
public boolean add(E e) {
ensureCapacity(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
public void ensureCapacity(int minCapacity) {
modCount++;
int oldCapacity = elementData.length;
if (minCapacity > oldCapacity) {
Object oldData[] = elementData;
int newCapacity = (oldCapacity * 3)/2 + 1;
if (newCapacity < minCapacity)
newCapacity = minCapacity;
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
}
新增元素:就是在陣列的某個位置記錄下元素的地址,新加入的元素時放在後面
擴容: 從原始碼可以看出,每次新增元素前會呼叫ensureCapacity進行一次擴容檢查,如果當前元素個數大於ArrayList的容量大小就會進行一次擴容,這也就是說ArrayList的底層是基於動態陣列實現的原因,具體來說:將新的陣列的容量擴容為之前的1.5倍的加1(jdk1.7中改為1.5倍擴容),在呼叫Arrays.copyOf將原來陣列的元素,複製到新的陣列,並將elementData引用指向它. 這個過程中Arrays.copyOf建立新的陣列,並複製元素,這就是為什麼說擴容很很費資源的原因.
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
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;
}
可能有人會問,為什麼要這樣擴容
1、如果一次性擴容擴得太大,必然造成記憶體空間的浪費
2、如果一次性擴容擴得不夠,那麼下一次擴容的操作必然比較快地會到來,這會降低程式執行效率,要知道擴容還是比價耗費效能的一個操作
所以擴容擴多少,是JDK開發人員在時間、空間上做的一個權衡,提供出來的一個比較合理的數值。
刪除元素
1. 按照元素的下標刪除
public E remove(int index) {
RangeCheck(index);
modCount++;
E oldValue = (E) elementData[index];
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // Let gc do its work
return oldValue;
}
2. 按照元素刪除,刪除首次出現的這個元素
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;
}
對於ArrayList來說,兩者幾乎差不多,都是呼叫下面一段程式碼
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // Let gc do its work
1. 將index+1開始的元素,向前移動一個位置
2. 將最後一個元素指定為null,可以讓gc去回收它
插入元素
public void add(int index, E element) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(
"Index: "+index+", Size: "+size);
ensureCapacity(size+1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
和直接add元素類似,也會先呼叫一次擴容方法,然後,將index開始的元素整體向後移動一位,最後在index位置存放元素地址
ArrayList fail-fast策略
ArrayList也採用了快速失敗的策略,通過記錄modCount(每次去修改ArrayList結構都會去修改這個值)來實現。在併發的情況下,使用迭代器迭代,會檢查modCount和expectedModCount是否相等,如果不相等,丟擲異常。
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
ArrayList的優缺點
1. ArrayList底層是陣列實現的,是一種隨機訪問模式,加上它實現了RandomAccess介面,因而查詢元素很快
2. ArrayList在順序新增一個元素,並且新增元素的個數不大於ArrayList的容量大小,這個操作只是會將元素的位置存放在陣列的索引位置,不會耗費什麼資源,會很快。(也就是如果能夠事先指定ArrayList的容量大小,順序新增元素很快)
3. ArrayList在刪除元素和插入元素的表現較差,如果元素很多,移動元素會很耗費資源
結論: ArrayList適合查詢元素和在指定容量的前提下,順序新增元素
ArrayList和Vector的區別
從ArrayList操作元素的方法可以看出,不是同步的,在多執行緒環境下,一定會出現執行緒安全問題,如果想要使用ArrayList又想要他執行緒安全怎麼辦?
一個方法是使用Collections.synchronizedList(List) 生成一個執行緒安全的list
public static <T> List<T> synchronizedList(List<T> list) {
return (list instanceof RandomAccess ?
new SynchronizedRandomAccessList<>(list) :
new SynchronizedList<>(list));
}
Vector 是ArrayList執行緒安全的版本,它的實現90%和ArrayList一樣,區別
1. 它是執行緒安全的,方法是同步的
2. 構造時候能夠指定增長因子capacityIncrement,如果不指定,擴容按照原來的兩倍擴容
private void ensureCapacityHelper(int minCapacity) {
int oldCapacity = elementData.length;
if (minCapacity > oldCapacity) {
Object[] oldData = elementData;
int newCapacity = (capacityIncrement > 0) ?
(oldCapacity + capacityIncrement) : (oldCapacity * 2);
if (newCapacity < minCapacity) {
newCapacity = minCapacity;
}
elementData = Arrays.copyOf(elementData, newCapacity);
}
}
另外,jdk1.7中提供一個ensureCapacity公開的方法,手動擴容,這個方法在新增大資料量前呼叫,能夠提高效率。
相關文章
- HashMap底層實現原理HashMap
- NSDictionary底層實現原理
- mysql索引底層實現MySql索引
- AutoreleasePool底層實現原理
- 死磕synchronized底層實現synchronized
- Python底層實現KNNPythonKNN
- MySQL索引底層實現原理MySql索引
- PHP 陣列底層實現PHP陣列
- LinkedList的底層實現
- ArrayList底層的實現原理
- 解析ArrayList的底層實現(上)
- MySQL Join的底層實現原理MySql
- Go語言interface底層實現Go
- Spring AOP概述、底層實現Spring
- 深入理解 MySQL 底層實現MySql
- 04 . Docker安全與Docker底層實現Docker
- 面試題深入解析:Synchronized底層實現面試題synchronized
- 深入解析 Go 中 Slice 底層實現Go
- 併發機制的底層實現
- php底層原理之陣列實現PHP陣列
- 橋接模式:探索JDBC底層實現橋接模式JDBC
- 深度解析HashMap底層實現架構HashMap架構
- Redis的底層實現---字串章節Redis字串
- 死磕Synchronized底層實現--概論synchronized
- 死磕Synchronized底層實現–偏向鎖synchronized
- 死磕Synchronized底層實現–概論synchronized
- Go語言map的底層實現Go
- KVO的使用和底層實現原理
- synchronized底層是怎麼實現的?synchronized
- javascript事件機制底層實現原理JavaScript事件
- String操作方法底層實現!!!
- session會話的底層實現方式Session會話
- iOS窺探KVO底層實現實戰篇iOS
- iOS底層-KVC使用實踐以及實現原理iOS
- iOS底層原理總結 -- 利用Runtime原始碼 分析Category的底層實現iOS原始碼Go
- Golang 定時器底層實現深度剖析Golang定時器
- React-Router底層原理分析與實現React
- 面試必問:HashMap 底層實現原理分析面試HashMap