Vector
public class Vector<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
protected Object[] elementData;
protected int elementCount;
}
这是两个核心变量,可以发现Vector
的底层容器是一个Object
数组
Vector
的常用方法
在老版本的JDK
中,synchronized
是一个超级重量的锁,需要去跟操作系统申请,再后来的版本中对synchronized
进行了各种优化,有了偏向锁、自旋锁、轻量级锁、重量级锁的四种锁升级的过程,导致synchronized
的也并不一定比JUC
包中各种CAS
锁效率低。
最让人迷惑的操作:为什么get()
方法也要加synchronized
?
由于各种操作都是用synchronized
关键字进行加锁,而且插入删除元素的时候都是需要把数组原封不动的复制一份,所以效率比较低下
/*
* add()方法最终调用
* */
public synchronized void insertElementAt(E obj, int index) {
...
// System.arraycopy 都是调用了这个函数,把数组原封不动的复制一份
if (s == elementData.length)
elementData = grow();
System.arraycopy(elementData, index,
elementData, index + 1,
s - index);
elementData[index] = obj;
elementCount = s + 1;
}
public synchronized int size() {
return elementCount;
}
public synchronized boolean removeElement(Object obj) {
...
int i = indexOf(obj);
if (i >= 0) {
// System.arraycopy 都是调用了这个函数,把数组原封不动的复制一份
removeElementAt(i);
return true;
}
return false;
}
/* 这个为什么都要弄成 synchronized */
public synchronized E get(int index) {
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
return elementData(index);
}
/* synchronized */
public synchronized E set(int index, E element) {
...
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
Stack
stack
是Vector
的子类,所有的栈的特殊增删改查操作都是封装Vector
的方法实现。
push
:最后一个位置放元素,调用add
peek
:返回elementCount - 1
位置的元素,调用elementAt
pop
:返回elementCount - 1
位置的元素,并删除最后一个元素
public synchronized E pop() {
E obj;
int len = size();
// 返回 peek 到的元素
obj = peek();
// 然后再删掉这个元素, System.arraycopy
removeElementAt(len - 1);
return obj;
}
AbstractList
核心变量
protected transient int modCount = 0;
用来标识增删改对已经创建的列表实例的修改次数。
在使用迭代器的时候回检测该值
就是说一个线程在使用迭代器的时候,暂时不允许对其进行增删改操作
如果这个检测到修改,就会抛出一个ConcurrentModificationException
ArrayList
跟Vector
最大的区别就是取消了所有的默认的synchronized
add()
:System.arraycopy
remove()
:System.arraycopy
size()
:没啥好说的
set()
:没啥好说的
内部类
private class Itr implements Iterator<E> {
int cursor; // 调用 next() 方法应该返回的元素
int lastRet = -1; // 上一次刚刚返回的元素,调用remove方法的时候用这个数进行移除操作
int expectedModCount = modCount; // 这个list对象 被修改的次数,每次增删改都会 + 1
// 但是在使用迭代器的时候,会检测该值是否被并发的修改
// 就是说一个线程在使用迭代器的时候,暂时不允许对其进行增删改操作
// 如果这个检测到修改,就会抛出一个 ConcurrentModificationException
// prevent creating a synthetic constructor
Itr() {}
public boolean hasNext() {
return cursor != size;
}
public E next() {
// 检查是否有并发修改当前列表, 有则抛出异常
checkForComodification();
int i = cursor;
//越界检查...
cursor = i + 1;
return (E) elementData[lastRet = i];
}
public void remove() {
//越界检查...
// 检查是否有并发修改当前列表, 有则抛出异常
checkForComodification();
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
优化
// 当数组容量撑到最大的时候需要进行扩容,新申请一个数组,并且全部把老数组的值拷贝到新数组
// 防止每次都重新分配,就会在分配时多分配几个格子,
// 而且每次分配都会越来越多
private Object[] grow(int minCapacity) {
return elementData = Arrays.copyOf(elementData,
// 分配的频繁,就会越来越一次性分配的越多
newCapacity(minCapacity));
}
private void fastRemove(Object[] es, int i) {
modCount++;
final int newSize;
// 如果删除的是最后一个, 直接置位null;不用array copy
// 不是最后一个 原地copy往前移动一格
if ((newSize = size - 1) > i)
System.arraycopy(es, i + 1, es, i, newSize - i);
es[size = newSize] = null;
}
LinkedList
内部类
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
// 外加内部迭代器类,
// 此时的内部迭代器类添加了
// hasPrevious()
// previousIndex()
// previous()
// 三个方法
// 一个线程在使用迭代器的时候,暂时不允许对其进行增删改操作
// 如果这个检测到修改,就会抛出一个 ConcurrentModificationException
常用方法
指定位置插入节点
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
// 主要说说这个东西吧,很为性能考虑的,在某个位置插入一个节点需要先找到这个位置的节点
// node(index) 返回指定的位置的节点
linkBefore(element, node(index));
}
// node(index) 返回指定的位置的节点
// get set remove (index)的时候都用到了这个操作,尽可能的加快查找效率
Node<E> node(int index) {
// (size >> 1) 相当于size / 2, 位运算速度更快
if (index < (size >> 1)) {
// 如果插入的位置小于 size 的 1/2,从头开始查找
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
// 否则从尾开始查找
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
IdentityArrayList
public void add(int index, E element) {
ensureCapacity(size+1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
public void ensureCapacity(int minCapacity) {
modCount++;
int oldCapacity = elementData.length;
if (minCapacity > oldCapacity) {
Object oldData[] = elementData;
// 每次扩展长度都扩 1.5 倍
int newCapacity = (oldCapacity * 3)/2 + 1;
if (newCapacity < minCapacity)
newCapacity = minCapacity;
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
}
IdentityLinkedList
这两个好像跟ArrayList
和LinkedList
没什么区别呀。
CopyOnWriteList
JDK 8
使用了JUC
包中的ReentrantLock
作为锁
JDK 11
使用了 synchronized
// setArray getArray
private transient volatile Object[] array;
public boolean add(E e) {
final ReentrantLock lock = this.lock;
// 添加元素的时候加锁,其他线程想添加只能等待锁释放
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
// 每次添加元素,只给数组的长度 + 1,也是调用了 System.arraycopy 进行重新分配一个数组,
// 并且把之前的值都拷贝过来,效率好低啊感觉,而且每次都只加1
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
// 把新创建出来的数组赋值给 array
setArray(newElements);
return true;
} finally {
lock.unlock();
}
// JDK 11
//synchronized (lock) {
// Object[] es = getArray();
// int len = es.length;
// es = Arrays.copyOf(es, len + 1);
// es[len] = e;
// setArray(es);
// return true;
//}
}
public E remove(int index) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
E oldValue = get(elements, index);
// 获取到要删除那个元素之后
int numMoved = len - index - 1;
// 如果删除的元素是第一个元素
if (numMoved == 0)
setArray(Arrays.copyOf(elements, len - 1));
else {
// 不是第一个元素就得把这个元素之前的元素和之后的元素拷贝到一个新数组
Object[] newElements = new Object[len - 1];
System.arraycopy(elements, 0, newElements, 0, index);
System.arraycopy(elements, index + 1, newElements, index,
numMoved);
setArray(newElements);
}
return oldValue;
} finally {
lock.unlock();
}
// JDK 11,使用了synchronized (lock) { ... }
}