本文已参与「新人创作礼」活动,一起开启掘金创作之路。
常用List
List特点:有序,元素可能重复
数据结构
数组
数组结构特点:适合随机读,写入性能比链表差
- ArrayList,并发不安全,读写均不加锁
- Vector,并发安全,读写均加锁
- CopyOnWriteArrayList,并发安全,读不加锁,写加锁
链表
数组结构特点:适合顺序读,写入性能比数组高
- LinkedList,并发不安全,读写均未加锁
写add
public void add(int index, E element) {
final ReentrantLock lock = this.lock;
// 1. 加锁
lock.lock();
try {
// 2. 获取数组快照
Object[] elements = getArray();
int len = elements.length;
if (index > len || index < 0)
throw new IndexOutOfBoundsException("Index: "+index+
", Size: "+len);
Object[] newElements;
int numMoved = len - index;
// 3. 如果插入指定的索引为尾部,则直接将老数组复制至新数组,并长度加1,待填充要插入的数据
if (numMoved == 0)
newElements = Arrays.copyOf(elements, len + 1);
else {
// 4. 如果插入指定的索引非尾部,则直接将老数组复制至新数组,并将指定的索引位置空出来,待填充要插入的数据
newElements = new Object[len + 1];
System.arraycopy(elements, 0, newElements, 0, index);
System.arraycopy(elements, index, newElements, index + 1,
numMoved);
}
// 5. 写入数据至新数组
newElements[index] = element;
// 6. 更新数组指针指向新复制出来的数组
setArray(newElements);
} finally {
// 7. 释放锁
lock.unlock();
}
}
读get
/**
* {@inheritDoc}
*
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E get(int index) {
// 1. 读取数组快照
return get(getArray(), index);
}
@SuppressWarnings("unchecked")
private E get(Object[] a, int index) {
// 2. 从数组快照中读取指定索引处的数据
return (E) a[index];
}
总结
CopyOnWriteArrayList适合读多写少的场景,读取数据不加锁,性能高于Vector,且线程并发安全,但读取数据的实时性比Vector差。写入性能相对于Vector差一些
CopyOnWriteArrayList由于其"读写分离"的思想,并发修改也不会抛出ConcurrentModificationException异常 主要缺点
- 内存成倍占用
- 写入数据与读取数据的实时性较差,由于每次都要先拷贝原数组数据再写入,实时性与数据量成反比
性能
排序均由高至低 随机读
- ArrayList,CopyOnWriteArrayList
- Vector
- LinkedList
顺序读
- ArrayList,CopyOnWriteArrayList,LinkedList
- Vector
写
- LinkedList
- ArrayList
- Vector
- CopyOnWriteArrayList