ArrayList的父类和实现的接口:
public class ArrayList extends AbstractList
implements List, RandomAccess, Cloneable, Serializable
ArrayList 官方API的一些介绍
ArrayList是一个实现了List接口的可变数组,实现了list的所有可选操作,容许存储所有的元素,包括null。
size, isEmpty, get, set, iterator, and listIterator operations操作时间复杂度都是常量(O(1)),添加n个元素需要O(n)的时间复杂度。其他的操作都是线性时间。
每个ArrayList实例都有容量。如果没有定义,ArrayList的初始容量是10,每次添加前都会使用ensureCapacity。
ArrayList是线程不安全的,如果多个线程同时使用arraylist, 必须在外部synchronized。一般通过同步封装list的object实现,如果没有的话,list必须用Collections.synchronizedList
来“封装”。
List list = Collections.synchronizedList(new ArrayList(...));
arrayList class iterator
and listIterator
返回的iterators采用了fail-fast的错误机制。当list的iterator产生之后,如果list在结构上有修改,就会抛出ConcurrentModificationException
异常。
ArrayList的一些理解
ArrayList基于数组实现,get和set的效率高。get和set会先进行范围的检查,然后get或者set数据
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
在数组的末尾添加的效率也高
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
但是如果不是在数组的末尾添加数据,需要使用System的arraycopy复制数组,效率不如LinkedList
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
实现了RandomAccess的接口,支持快速的随机访问。
for (int i=0, n=list.size(); i < n; i++) {
list.get(i);
}
runs faster than this loop:
for (Iterator i=list.iterator(); i.hasNext(); ) {
i.next();
}
ArrayList的线程不安全
ArrayList在性能上比Vector好,但是由于ArrayList的add,remove方法都没有synchronized,所以在多线程操作ArrayList的时候可能会造成得不到想要的结果
public class ArrayListTest {
public static void main(String[] args) throws InterruptedException {
List<Integer> list = new ArrayList<Integer>();
MyTask3 task = new MyTask3(list);
for(int i = 0 ; i < 1000; i++) {
new Thread(task).start();
}
System.out.println(list.size());
}
}
class MyTask3 implements Runnable {
List<Integer> list;
public MyTask3(List<Integer> list) {
this.list = list;
}
@Override
public void run() {
for(int i = 0 ; i < 100; i++) {
list.add(1);
}
}
}
执行以上代码五次的结果:
99999
100000
99900
Exception in thread "Thread-2" java.lang.ArrayIndexOutOfBoundsException
99873
加上synchronized之后:
public void run() {
for(int i = 0 ; i < 100; i++) {
synchronized (list) {
list.add(1);
}
}
}
执行五次
95200
99900
99700
99900
100000
还是没有达到想要的结果。这里我的推测是在其他线程还没有执行完的时候,主线程就执行了System.out.Println。于是在这里加上CountDownLatch。
public class ArrayListTest {
public static void main(String[] args) throws InterruptedException {
List<Integer> list = new ArrayList<Integer>();
CountDownLatch countDownLatch = new CountDownLatch(1000);
MyTask3 task = new MyTask3(list, countDownLatch);
for(int i = 0 ; i < 1000; i++) {
new Thread(task).start();
}
countDownLatch.await();
System.out.println(list.size());
}
}
class MyTask3 implements Runnable {
List<Integer> list;
CountDownLatch countDownLatch;
public MyTask3(List<Integer> list, CountDownLatch countDownLatch) {
this.list = list;
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
for(int i = 0 ; i < 100; i++) {
synchronized (list) {
list.add(1);
}
}
countDownLatch.countDown();
}
}
再执行五次:
100000
100000
100000
100000
100000
结果完全正确。
以上是我基于ArrayList的一些简单的理解。 站在巨人的肩膀上才能看的更高,其中也引用了前辈们的一些例子和方法。深深的感觉在技术这条道路上还有很多值得深入研究和学习的地方,接下来准备深入研究一下多线程的相关东西。