ArrayList源码
transient Object[] elementData;
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
默认大小空,什么时候开始有大小?
add方法
//这里看到add方法没有加任何锁,所以线程不安全的
public boolean add(E e) {
//size初始值0,这里开始+1,这里判断开始扩容
ensureCapacityInternal(size + 1); // Increments modCount!!
//将数据放到集合中
elementData[size++] = e;
return true;
}
//这里如果elementData=DEFAULTCAPACITY_EMPTY_ELEMENTDATA都是空,默认10
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
//源码默认是10大小
private static final int DEFAULT_CAPACITY = 10;
扩容
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//这里10+原来的一半,即为15,右移1位,相当于10除以2的一次幂,即为5(左移右移是最快的)
//这里进行右移(>>)右移几位相当于除以2的几次幂;左移几位相当于乘以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);
}
线程不安全,add方法没有加锁,为了保证并发效率。
多线程调用add方法会报错 ConcurrentModificationException并发修改异常
所以ArrayList是线程不安全的。可以看到源码里面没有任何线程安全的代码。
ArrayList线程不安全,如何解决
- Vector线程安全,代码里加了synchonized关键字。但是Vector性能低,不用它
- Collections.synchronizedList 不用
重点是下面这个
CopyOnWriteArrayList集合
CopyOnWriteArrayList集合,JUC中的类。写时复制集合类。
写时复制,主要是读写分离的思想。
底层加了ReentrantLock锁。
不是直接往当前容器object【】添加,而是先将当前容器进行复制copy,得倒一个新的object[],然后向新的容器添加元素,添加完成后将原来容器引用指向新的容器。
这样好处是可以对容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素
//˙这里volatile关键字
private transient volatile Object[] array;
final Object[] getArray() {
return array;
}
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
//又看到了copyof方法,复制出一个新的容器,向新的容器添加元素
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
//将原来容器引用指向新的容器
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
结构–底层数组
可以看到,底层是数组,只不过这个ArrayList可可调整大小。而数组却是固定的大小,不能调整大小。说白了ArrayList集合就是可以调整大小的数组。(扩容)
特点–查询快,增删慢
数组有什么特点?
增删慢:每次删除元素,都需要更改数组长度、拷贝以及移动元素位置。
查询快:由于数组在内存中是一块连续空间,因此可以根据地址+索引的方式快速获取对应位置上的元素。
所以ArrayList的特点就是查询快,增删慢。
ArrayList总结:
ArrayList是基于动态数组实现的,在增删时候,需要数组的拷⻉复制,copyof方法。
ArrayList的默认初始化容量是10,每次扩容时候增加原先容量的⼀半,也就是变为原来的1.5倍
删除元素时不会减少容量,若希望减少容量则调⽤trimToSize()
它不是线程安全的。它能存放null值。
————————————————
ArrayList和LinkList区别?
ArrayList
基于动态数组的数据结构
对于随机访问的set和get,ArrayList要优于LinkedList
对于随机操作的add和remove,ArrayList不一定比LinkedList慢(ArrayList底层是由动态数组,因此并不是每次add和remove的时候都需要创建新数组)
LinkedList
基于链表的数据结构
对于顺序操作,LinkedList不一定比ArrayList慢
对于随机操作,LinkedList效率明显较低