手寫 | ArrayList集合
底層採用數組方式
怎樣保證集合存放無限大小—數組擴容技術
代碼實現:
package MyArraayList;
import java.util.Arrays;
/**
* @author 孫一鳴 on 2020/3/3
*/
public class ExtArrayList {
//定義一個數組,存放數據
private Object[] elementData;
//默認初始化數組容量大小
private static final int DEFAULT_CAPACITY = 10;
//空數組
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//記錄ArrayList大小
private int size;
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
//無參構造,默認初始化容量爲10
public ExtArrayList() throws Exception {
//elementData = new Object[DEFAULT_CAPACITY];
this(DEFAULT_CAPACITY);
}
//有參構造
public ExtArrayList(int initialCapacity) throws Exception {
if (initialCapacity < 0) {
throw new Exception("初始化數組不能小於0");
}
this.elementData = new Object[initialCapacity];
}
public void add(int index, Object element) {
//擴容
ensureCapacityInternal(size + 1); // Increments modCount!!
//將index後面的對象,移動到index+1的位置上,將需要添加的值覆蓋
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
public void add(Object object) {
//如果給ArrayList添加對象,先判斷容量是否需要擴容
ensureCapacityInternal(size + 1);
//添加信息
elementData[size++] = object;
/* //老數組容量大小
int oldCapacity = elementData.length;
//新數組容量大小
int newCapacity;
//判斷數組實際容量是否 > elementData容量
if (size == oldCapacity) {
//定義新數組容量大小
newCapacity = 2 * size;
//將老數組的數據寫入到新數組中
Object[] newObjects = new Object[newCapacity];
for (int i = 0; i < oldCapacity; i++) {
newObjects[i] = elementData[i];
}
elementData = newObjects;
}*/
}
/*
* 方法:擴容
* 參數:minCapacity 最小擴容容量
* */
public void ensureCapacityInternal(int minCapacity) {
//如果當前存放數據的數組 爲一個空數組,那麼他擴容的大小爲:
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//最小擴容容量:數組默認大小值與參數比較
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
//老數組容量大小
int oldCapacity = elementData.length;
//新數組容量大小
int newCapacity = oldCapacity + (oldCapacity >> 1);
//如果新數組容量大小 小於最小擴容容量
if (newCapacity < minCapacity)
newCapacity = minCapacity;//最少保證容量和 最小擴容容量 一樣
//如果新數組容量大小 大於 最大容容量
if (newCapacity > MAX_ARRAY_SIZE)
newCapacity = (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
elementData = Arrays.copyOf(elementData, newCapacity);
}
public Object get(int index) {
if (index >= size)
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
return elementData[index];
}
/*
String[] arr = {"A","B","C","D","E","F"};
System.arraycopy(arr ,3,arr,2,2);
從下標爲3的位置開始複製,複製的長度爲2(複製D、E),從下標爲2的位置開始替換爲D、E
複製後的數組爲String[] arr = {"A","B","D","E","E","F"};
刪除下標爲i=2的元素(c):
從下標爲index+1=3的元素(D)copy ,
從index=2的位置替換
複製長度:numMoved=(Array.length-index-1=3)個,
* */
public Object remove(int index) {
if (index > size || index < 0) {
throw new IndexOutOfBoundsException("刪除越界。。");
}
int numMoved = size - index - 1;
System.arraycopy(elementData, index + 1, elementData, index,
numMoved);
Object o = get(index);
//將最後一個元素爲空
elementData[--size] = null;
return o;
}
//刪除對象
public boolean remove(Object o) {
for (int index = 0; index < size; index++) {
if (o.equals(elementData[index])) {
remove(index);
return true;
}
}
return false;
}
}
Vector底層實現原理
Vector是線程安全的,但是性能比ArrayList要低。
ArrayList,Vector主要區別爲以下幾點:
(1):Vector是線程安全的,源碼中有很多的synchronized可以看出,而ArrayList不是。導致Vector效率無法和ArrayList相比;
(2):ArrayList和Vector都採用線性連續存儲空間,當存儲空間不足的時候,ArrayList默認增加爲原來的50%,Vector默認增加爲原來的一倍;
(3):Vector可以設置capacityIncrement,而ArrayList不可以,從字面理解就是capacity容量,Increment增加,容量增長的參數。
演示多線程下ArrayList不安全問題
案例:
public static void main(String[] args) {
// TODO Auto-generated method stub
final ArrayList<String> arrayList = new ArrayList<String>();
for (int i = 0; i < 3; i++) {
new Thread(new Runnable() {
@Override
public void run() {
arrayList.add("aaaaa");
System.out.println(arrayList);
}
}).start();
}
}
}
錯誤截圖:
導致原因:
併發爭搶修改導致,參考花名冊簽名情況。
一個人正在寫入,另外一個同學過來搶奪,導致數據不一致異常
解決方案:
方案一:
final List arrayList = new Vector();
使用vector類vector的add方法加鎖,使數據性一致,但併發性下降</font>
方案二:
final List<String> arrayList =
Collections.synchronizedList(new ArrayList<String>());Collections工具類
方案三:
final List<String> arrayList =new CopyOnWriteArrayList<>();
CopyOnWrite容器即寫時複製的容器。往一個容器添加元素的時候,不直接往當前容器object[]添加,而是先將當前容器object[]進Copy,複製出一個新的容器object[] newELements,然後向新的容器object[] newELements 裏添加元素,添加完元素之後,再將原容器的引用指向新的容器setArray(newELements);.這樣做的好處是可以對CopyonWrite容器進行併發的讀,而不需要加鎖,因爲當前容器不會添加任何元素。所以CopyOnWrite 容器也是一. 種讀寫分離的思想,讀和寫不同的容器
CopyOnWriteArrayList的Add方法
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}