手写 | 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();
}
}