/**
* 将指定的元素追加到此列表的末尾。
*
* @param e element to be appended to this list
* @return true (as specified by {@link Collection#add})
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
/**
* 增加容量以确保它至少可以容纳
* 最小容量参数指定的元素数。
*
* @param minCapacity the desired minimum capacity
*/
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//这里采用了位运算,右移移位,相当于除以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);
}
可以看到其实我们并没有在创建时指定其长度大小,那它是怎么做到的呢?哈哈,别急,我们现在就去探索其中的原由
/**
* 构造一个初始容量为10的空列表
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
我们再点DEFAULTCAPACITY_EMPTY_ELEMENTDATA进去看看
/**
* Shared empty array instance used for default sized empty instances. We
* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
* first element is added.
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
也就是说当我们没有去指定ArrayList的初始长度时,数组的长度默认是0,当我们向ArrayList中add元素时,才分配一个默认长度为10的初试容量
/**
* Constructs an empty list with the specified initial capacity.
*
* @param initialCapacity the initial capacity of the list
* @throws IllegalArgumentException if the specified initial capacity
* is negative
*/
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 static void main(String[] args) throws InterruptedException {
List<Object> arrayList = new ArrayList<>();
for (int i = 0; i < 20000; i++) {
new Thread(()->{arrayList.add(Thread.currentThread().getName());}).start();
}
Thread.sleep(3000);
System.out.println(arrayList.size());
}
经过多次运行,其输出结果分别为18842、19997、19996等,可见输出的结果不仅不一样,而且并不是我们想要得到的结果(20000)
1.加同步锁synchronized
public static void main(String[] args) throws InterruptedException {
List<String> list = new ArrayList<>();
for (int i = 0; i < 20000; i++) {
new Thread(()->{
synchronized (list) {
list.add(Thread.currentThread().getName());
}
}).start();
}
Thread.sleep(3000);//由于计算机运行速度过快,这里不进行阻塞的话在list还没有add完元素之前就已经执行主线程的输出语句了(这样的结果是一点会小于20000的)
System.out.println(list.size());
}
结论:这样可以达到我们ArrayList线程安全的效果,但是加synchronized同步锁的话会降低系统的并发性,效率低,不推荐使用。
2.使用JUC安全类型的集合CopyOnWriteArrayList
public static void main(String[] args) throws InterruptedException {
CopyOnWriteArrayList<String> copyOnWriteArrayList = new CopyOnWriteArrayList<>();
for (int i = 0; i < 20000; i++) {
new Thread(()->{
copyOnWriteArrayList.add(Thread.currentThread().getName());
}).start();
}
Thread.sleep(3000);
System.out.println(copyOnWriteArrayList.size());
}
结论:使用JUC中并发包的CopyOnWriteArrayList类可以达到我们所期望的结果,推荐使用。
3.ArrayList是继承自AbstractList类,而AbstractList实现了List接口,List接口又继承了Collection类,我们知道Collections是我们Collection集合类的一个帮助类,提供了一系列静态方法,用于对集合中元素进行排序、搜索以及线程安全等各种操作。因此,我们可以使用它为我们提供的静态方法来实现线程安全操作
public static void main(String[] args) throws InterruptedException {
List<String> list = Collections.synchronizedList(new ArrayList<>());
for (int i = 0; i < 20000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
Thread.sleep(3000);
System.out.println(list.size());
}
结论:使用Collections工具类所提供的静态方法可以保证对一些集合操作线程安全性,推荐使用。