使用modCount记录list发生变化(增加删除)的次数
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
private void readObject(java.io.ObjectInputStream s)
private void writeObject(java.io.ObjectOutputStream s)
相同点:
不同点:
若index < 双向链表长度的1/2,则从前向后查找; 否则,从后向前查找。
public E get(int index)
{
checkElementIndex(index);
return node(index).item;
}
Node<E> node(int index)
{
// assert isElementIndex(index);
if (index < (size >> 1)) {
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;
}
}
public class CopyOnWriteArrayList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
final transient ReentrantLock lock = new ReentrantLock();
private transient volatile Object[] array;
public CopyOnWriteArrayList() // 构造一个空数组 setArray(new Object[0]);
public CopyOnWriteArrayList(Collection<? extends E> c) // 将 传入的 Collection 转为Object[] 赋值给 array
public CopyOnWriteArrayList(E[] toCopyIn) // 将传入的数组 toCopyIn 赋值给 array
}
先将数组拷贝到一个容量为之前数组容量+1的数组中,其他线程如果并发遍历的时候,可能就是遍历的是原数组,而不是新的数组。
public boolean add(E e)
public void add(int index, E element) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
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;
if (numMoved == 0) //插在末尾
newElements = Arrays.copyOf(elements, len + 1);
else {
newElements = new Object[len + 1];
System.arraycopy(elements, 0, newElements, 0, index);
System.arraycopy(elements, index, newElements, index + 1,
numMoved);
}
newElements[index] = element;
setArray(newElements);
} finally {
lock.unlock();
}
}
public boolean addIfAbsent(E e)
private boolean addIfAbsent(E e, Object[] snapshot) ---加锁的添加方法,详情看 jdk souorceCode注释
remove和add一样,先复制再删除再赋值。
public E get(int index) {
return get(getArray(), index);
}
参考链接
自己整理的博客
====
数组+红黑树+单链表+双向链表
要重写下面的这个函数才能实现LRU,比如重写成:
public boolean removeEldestEntry(Map.Entry<K,V>eldest)
{
// 当元素个数大于了缓存的容量, 就移除元素
return size()>this.capacity;
}
参考博客
public class LinkedHashMap<K,V>
extends HashMap<K,V>
implements Map<K,V>
//HashMap 中,这三个方法都是没实现的,在 LinkedHashMap 中实现来维护结点顺序
//
void afterNodeAccess(Node<K,V> p) { }
void afterNodeInsertion(boolean evict) { }
void afterNodeRemoval(Node<K,V> p) { }
//LinkedHashMap
/*
在节点访问之后被调用,主要在put()已经存在的元素或get()时被调用,
如果accessOrder为true,调用这个方法把访问到的节点移动到双向链表的末尾。
*/
void afterNodeAccess(Node<K,V> e) { // move node to last
LinkedHashMap.Entry<K,V> last;
// accessOrder = true则执行,否则结束
// accessOrder = true, e 不是 tail 尾结点
if (accessOrder && (last = tail) != e) {
LinkedHashMap.Entry<K,V> p =
(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
p.after = null;
if (b == null)
head = a;
else
b.after = a;
if (a != null)
a.before = b;
else
last = b;
if (last == null)
head = p;
else {
p.before = last;
last.after = p;
}
tail = p;
++modCount;
}
}
/*
在节点插入之后做些什么,在HashMap中的putVal()方法中被调用,可以看到HashMap中这个方法的实现为空。
evict:驱逐的意思
如果 evict 为 true,则移除最老的元素(head)
默认removeEldestEntry()方法返回false,也就是不删除元素。
*/
void afterNodeInsertion(boolean evict) { // possibly remove eldest
LinkedHashMap.Entry<K,V> first;
//如果evict为true,且头节点不为空,且 确定移除最老的元素,即移除 head
//head 为 双向链表的头结点
if (evict && (first = head) != null && removeEldestEntry(first)) {
K key = first.key;
//HashMap.removeNode()从HashMap中把这个节点移除之后,会调用 afterNodeRemoval() 方法;
removeNode(hash(key), key, null, false, true);
}
}
//传进来的参数 是 双向链表的头结点 (即最老的结点)
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
return false;
}
/*
在节点被删除之后调用的方法 afterNodeInsertion -> HashMap.removeNode() -> afterNodeRemoval
从双向链表中 删除结点 e
*/
void afterNodeRemoval(Node<K,V> e) { // unlink
LinkedHashMap.Entry<K,V> p =
(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
// 把节点p从双向链表中删除。
p.before = p.after = null;
if (b == null)
head = a;
else
b.after = a;
if (a == null)
tail = b;
else
a.before = b;
}
/*
因此使用 LinkedHashMap 实现 LRU,
1) 设置 accessOrder 为 true --> 把最近访问的结点移动到尾部
2) 重写 removeEldestEntry 方法 --> 返回 true 会删除该结点, false 不删除
*/
(1)LinkedHashMap继承自HashMap,具有HashMap的所有特性;
(2)LinkedHashMap内部维护了一个双向链表存储所有的元素;
(3)如果accessOrder为false,则可以按插入元素的顺序遍历元素;
(4)如果accessOrder为true,则可以按访问元素的顺序遍历元素;
(5)LinkedHashMap的实现非常精妙,很多方法都是在HashMap中留的钩子(Hook),直接实现这些Hook就可以实现对应的功能了,并不需要再重写put()等方法;
(6)默认的LinkedHashMap并不会移除旧元素,如果需要移除旧元素,则需要重写removeEldestEntry()方法设定移除策略;
(7)LinkedHashMap可以用来实现LRU缓存淘汰策略;
LinkedHashMap如何实现LRU缓存淘汰策略呢?
首先,我们先来看看LRU是个什么鬼。LRU,Least Recently Used,最近最少使用,也就是优先淘汰最近最少使用的元素。
如果使用LinkedHashMap,我们把accessOrder设置为true是不是就差不多能实现这个策略了呢?答案是肯定的。请看下面的代码:
public class LRUTest
{
public static void main(String[] args)
{
LRU<Integer,Integer> lru = new LRU(5,0.75f);
lru.put(1,1);
lru.put(2,2);
lru.put(3,3);
lru.put(4,4);
lru.put(5,5);
lru.put(6,6);
lru.put(7,7);
System.out.println(lru.get(4));
lru.put(6,666);
System.out.println(lru);
}
}
class LRU extends LinkedHashMap<K,V>
{
private int capacity;
public LRU(int capacity,int loadFactor)
{
super(capacity,loadFactor,true);
this.capacity = capacity;
}
/**
* 重写removeEldestEntry()方法设置何时移除旧元素
* @param eldest
* @return
*/
public boolean removeEldestEntry(Map.Entry<K,V>eldest)
{
// 当元素个数大于了缓存的容量, 就移除元素
return size()>this.capacity;
}
}