package java.util;
import java.util.function.Consumer;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.io.IOException;
public class LinkedHashMap
extends HashMap
implements Map
{
//LinkedHashMap æ©å±äº HashMap.Node ç±»
//å¨å
¶åºç¡ä¸æ°å¢äºä¸¤ä¸ªæååéç¨äºæå®ä¸ä¸ä¸ªç»ç¹ before åä¸ä¸ä¸ªç»ç¹ after
static class Entry extends HashMap.Node {
Entry before, after;
Entry(int hash, K key, V value, Node next) {
super(hash, key, value, next);
}
}
//åºååID
private static final long serialVersionUID = 3801124242820219131L;
//æåååé¾è¡¨ç头ç»ç¹
transient LinkedHashMap.Entry head;
//æåææ°æå
¥çä¸ä¸ªç»ç¹
transient LinkedHashMap.Entry tail;
//å¦æä¸ºtrueï¼åå
é¨å
ç´ æç
§è®¿é®é¡ºåºæåº
//å¦æä¸ºfalseï¼åå
é¨å
ç´ æç
§æå
¥é¡ºåºæåº
final boolean accessOrder;
//å°ç»ç¹ p æå
¥é¾è¡¨å°¾é¨
private void linkNodeLast(LinkedHashMap.Entry p) {
LinkedHashMap.Entry last = tail;
tail = p;
//妿 last == nullï¼è¯´ææ¤åé¾è¡¨ä¸ºç©ºï¼å头ç»ç¹åºä¸º p
if (last == null)
head = p;
else {
//æ´æ°ç»ç¹é´çå¼ç¨
p.before = last;
last.after = p;
}
}
//å°ç»ç¹ src æ¿æ¢ä¸º dst
private void transferLinks(LinkedHashMap.Entry src, LinkedHashMap.Entry dst) {
//ï¼b å dst çä¸ä¸ä¸ªç»ç¹ï¼æå src çä¸ä¸ä¸ªç»ç¹
//ï¼a å dst çä¸ä¸ä¸ªç»ç¹ï¼æå src çä¸ä¸ä¸ªç»ç¹
LinkedHashMap.Entry b = dst.before = src.before;
LinkedHashMap.Entry a = dst.after = src.after;
if (b == null)
head = dst;
else
b.after = dst;
if (a == null)
tail = dst;
else
a.before = dst;
}
//éç½®
void reinitialize() {
super.reinitialize();
head = tail = null;
}
//éè½½ç¶ç±» HashMap çæ¹æ³
//æå»ºä¸ä¸ªæ°ç»ç¹ï¼å¹¶å°ç»ç¹æå
¥å°é¾è¡¨å°¾é¨
Node newNode(int hash, K key, V value, Node e) {
LinkedHashMap.Entry p =
new LinkedHashMap.Entry(hash, key, value, e);
linkNodeLast(p);
return p;
}
//éè½½ç¶ç±» HashMap çæ¹æ³
//ç¨äºå° TreeNodes 转æ¢å°æ®éèç¹
Node replacementNode(Node p, Node next) {
LinkedHashMap.Entry q = (LinkedHashMap.Entry)p;
LinkedHashMap.Entry t =
new LinkedHashMap.Entry(q.hash, q.key, q.value, next);
transferLinks(q, t);
return t;
}
//éè½½ç¶ç±» HashMap çæ¹æ³
//æå»ºä¸ä¸ªæ°ççº¢é»æ ç»ç¹ï¼å¹¶å°ç»ç¹æå
¥å°é¾è¡¨å°¾é¨
TreeNode newTreeNode(int hash, K key, V value, Node next) {
TreeNode p = new TreeNode(hash, key, value, next);
linkNodeLast(p);
return p;
}
//éè½½ç¶ç±» HashMap çæ¹æ³
//便®å½åç»ç¹æé ä¸ä¸ªæ ç»ç¹
TreeNode replacementTreeNode(Node p, Node next) {
LinkedHashMap.Entry q = (LinkedHashMap.Entry)p;
TreeNode t = new TreeNode(q.hash, q.key, q.value, next);
transferLinks(q, t);
return t;
}
//å¨ç§»é¤ç»ç¹ e åè°ç¨
void afterNodeRemoval(Node e) {
//ç»ç¹ b æåç»ç¹ e çä¸ä¸ä¸ªç»ç¹ï¼ç»ç¹ a æåç»ç¹ e çä¸ä¸ä¸ªç»ç¹
LinkedHashMap.Entry p = (LinkedHashMap.Entry)e, b = p.before, a = p.after;
//ç§»é¤ç»ç¹ p 对ç¸é»ç»ç¹çå¼ç¨
p.before = p.after = null;
//妿 b == nullï¼è¯´æç»ç¹ p æ¯åé¾è¡¨ç头ç»ç¹ï¼åç§»é¤ç»ç¹ p åæ°ç头ç»ç¹æ¯ a
//妿 b != nullï¼åæ´æ°ç»ç¹é´çå¼ç¨
if (b == null)
head = a;
else
b.after = a;
//妿 a == nullï¼è¯´æç»ç¹ a æ¯å°¾ç»ç¹ï¼åç§»é¤ç»ç¹ p åææ°ä¸ä¸ªè®¿é®çç»ç¹å°±æ¯ååæ°ç¬¬äºçç»ç¹
//妿 a != nullï¼åæ´æ°ç»ç¹é´çå¼ç¨
if (a == null)
tail = b;
else
a.before = b;
}
//卿å
¥å
ç´ åè°ç¨ï¼æ¤æ¹æ³å¯ç¨äº LRUcache ç®æ³ä¸ç§»é¤æè¿æå°ä½¿ç¨çå
ç´
void afterNodeInsertion(boolean evict) {
LinkedHashMap.Entry first;
if (evict && (first = head) != null && removeEldestEntry(first)) {
K key = first.key;
removeNode(hash(key), key, null, false, true);
}
}
//å½è®¿é®äºç»ç¹ e æ¶è°ç¨
//ç»ç¹ e æ¯ææ°è®¿é®çä¸ä¸ªç»ç¹ï¼æ¤å¤å°ç»ç¹ e 置为é¾è¡¨çå°¾ç»ç¹
void afterNodeAccess(Node e) {
//last ç¨æ¥æåé¾è¡¨çå°¾ç»ç¹
LinkedHashMap.Entry last;
//åªæå½ä¸ä¸æ¬¡è®¿é®çç»ç¹ä¸æ¯ç»ç¹ e æ¶ï¼(last = tail) != eï¼ï¼æéè¦è¿è¡ä¸ä¸æ¥æä½
if (accessOrder && (last = tail) != e) {
//p æ¯ææ°è®¿é®çä¸ä¸ªç»ç¹ï¼b æ¯ç»ç¹ p çä¸ä¸ä¸ªç»ç¹ï¼a æ¯ç»ç¹ p çä¸ä¸ä¸ªç»ç¹
LinkedHashMap.Entry p = (LinkedHashMap.Entry)e, b = p.before, a = p.after;
//å 为ç»ç¹ p å°æä¸ºå°¾ç»ç¹ï¼æä»¥ after 置为null
p.after = null;
//妿 b == null ï¼è¯´æç»ç¹ p æ¯åé¾è¡¨ç头ç»ç¹ï¼åæ¤æ¶å° head æåä¸ä¸ä¸ªç»ç¹ a
//妿 b != null ï¼åç§»é¤ç»ç¹ b 对ç»ç¹ p çå¼ç¨
if (b == null)
head = a;
else
b.after = a;
//妿 a !=nullï¼è¯´æç»ç¹ p 䏿¯åé¾è¡¨çå°¾ç»ç¹ï¼åç§»é¤ç»ç¹ a 对ç»ç¹ p çå¼ç¨
//妿 a == nullï¼å说æç»ç¹ p æ¯åé¾è¡¨çå°¾ç»ç¹ï¼å让 last æåç»ç¹ b
if (a != null)
a.before = b;
else
last = b;
//妿 last == nullï¼è¯´æåé¾è¡¨ä¸ºç©ºï¼åæ¤æ¶å¤´ç»ç¹å°±æ¯ç»ç¹ p
//妿 last != nullï¼åå»ºç« last åå®é
å°¾ç»ç¹ p ä¹é´çå¼ç¨
if (last == null)
head = p;
else {
p.before = last;
last.after = p;
}
//ææ°ä¸ä¸ªå¼ç¨å°çç»ç¹å°±æ¯ tail
tail = p;
++modCount;
}
}
void internalWriteEntries(java.io.ObjectOutputStream s) throws IOException {
for (LinkedHashMap.Entry e = head; e != null; e = e.after) {
s.writeObject(e.key);
s.writeObject(e.value);
}
}
//èªå®ä¹åå§å®¹éä¸è£
è½½å å
//å
é¨å
ç´ æç
§æå
¥é¡ºåºè¿è¡æåº
public LinkedHashMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
accessOrder = false;
}
//èªå®ä¹è£
è½½å å
//å
é¨å
ç´ æç
§æå
¥é¡ºåºè¿è¡æåº
public LinkedHashMap(int initialCapacity) {
super(initialCapacity);
accessOrder = false;
}
//使ç¨é»è®¤çåå§å®¹é以åè£
è½½å å
//å
é¨å
ç´ æç
§æå
¥é¡ºåºè¿è¡æåº
public LinkedHashMap() {
super();
accessOrder = false;
}
//使ç¨åå§æ°æ®
//å
é¨å
ç´ æç
§æå
¥é¡ºåºè¿è¡æåº
public LinkedHashMap(Map extends K, ? extends V> m) {
super();
accessOrder = false;
putMapEntries(m, false);
}
/**
* @param initialCapacity åå§å®¹é
* @param loadFactor è£
è½½å å
* @param accessOrder å¦æä¸ºtrueï¼åå
é¨å
ç´ æç
§è®¿é®é¡ºåºæåºï¼å¦æä¸ºfalseï¼åå
é¨å
ç´ æç
§æå
¥é¡ºåºæåº
*/
public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) {
super(initialCapacity, loadFactor);
this.accessOrder = accessOrder;
}
//夿é忝å¦å
å«å¼ä¸º value çé®å¼å¯¹
public boolean containsValue(Object value) {
for (LinkedHashMap.Entry e = head; e != null; e = e.after) {
V v = e.value;
if (v == value || (value != null && value.equals(v)))
return true;
}
return false;
}
//è·åé®å¼ä¸º key çé®å¼å¯¹ç value
public V get(Object key) {
Node e;
if ((e = getNode(hash(key), key)) == null)
return null;
if (accessOrder)
afterNodeAccess(e);
return e.value;
}
//è·åé®å¼ä¸º key çé®å¼å¯¹ç valueï¼å¦æ key ä¸åå¨ï¼åè¿åé»è®¤å¼ defaultValue
public V getOrDefault(Object key, V defaultValue) {
Node e;
if ((e = getNode(hash(key), key)) == null)
return defaultValue;
if (accessOrder)
afterNodeAccess(e);
return e.value;
}
//æ¸
空éåå
ç´
public void clear() {
super.clear();
head = tail = null;
}
//妿卿é 彿°ä¸åæ° accessOrder ä¼ å
¥äº true ï¼åé¾è¡¨å°æç
§è®¿é®é¡ºåºæ¥æå
//峿æ°è®¿é®çç»ç¹å°å¤äºé¾è¡¨çå°¾é¨ï¼ä¾æ¤å¯ä»¥æ¥æå»º LRUcache ç¼å
//æ¤æ¹æ³å°±ç¨äºå³å®æ¯å¦ç§»é¤ææ§çç¼åï¼é»è®¤è¿å false
protected boolean removeEldestEntry(Map.Entry eldest) {
return false;
}
//ç¨äºè·åéåä¸çææé®
public Set keySet() {
Set ks = keySet;
if (ks == null) {
ks = new LinkedKeySet();
keySet = ks;
}
return ks;
}
final class LinkedKeySet extends AbstractSet {
public final int size() { return size; }
public final void clear() { LinkedHashMap.this.clear(); }
public final Iterator iterator() {
return new LinkedKeyIterator();
}
public final boolean contains(Object o) { return containsKey(o); }
public final boolean remove(Object key) {
return removeNode(hash(key), key, null, false, true) != null;
}
public final Spliterator spliterator() {
return Spliterators.spliterator(this, Spliterator.SIZED |
Spliterator.ORDERED |
Spliterator.DISTINCT);
}
public final void forEach(Consumer super K> action) {
if (action == null)
throw new NullPointerException();
int mc = modCount;
for (LinkedHashMap.Entry e = head; e != null; e = e.after)
action.accept(e.key);
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
//ç¨äºè·åéåä¸çææå¼
public Collection values() {
Collection vs = values;
if (vs == null) {
vs = new LinkedValues();
values = vs;
}
return vs;
}
final class LinkedValues extends AbstractCollection {
public final int size() { return size; }
public final void clear() { LinkedHashMap.this.clear(); }
public final Iterator iterator() {
return new LinkedValueIterator();
}
public final boolean contains(Object o) { return containsValue(o); }
public final Spliterator spliterator() {
return Spliterators.spliterator(this, Spliterator.SIZED |
Spliterator.ORDERED);
}
public final void forEach(Consumer super V> action) {
if (action == null)
throw new NullPointerException();
int mc = modCount;
for (LinkedHashMap.Entry e = head; e != null; e = e.after)
action.accept(e.value);
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
/**
* Returns a {@link Set} view of the mappings contained in this map.
* The set is backed by the map, so changes to the map are
* reflected in the set, and vice-versa. If the map is modified
* while an iteration over the set is in progress (except through
* the iterator's own remove operation, or through the
* setValue operation on a map entry returned by the
* iterator) the results of the iteration are undefined. The set
* supports element removal, which removes the corresponding
* mapping from the map, via the Iterator.remove,
* Set.remove, removeAll, retainAll and
* clear operations. It does not support the
* add or addAll operations.
* Its {@link Spliterator} typically provides faster sequential
* performance but much poorer parallel performance than that of
* {@code HashMap}.
*
* @return a set view of the mappings contained in this map
*/
public Set> entrySet() {
Set> es;
return (es = entrySet) == null ? (entrySet = new LinkedEntrySet()) : es;
}
final class LinkedEntrySet extends AbstractSet> {
public final int size() { return size; }
public final void clear() { LinkedHashMap.this.clear(); }
public final Iterator> iterator() {
return new LinkedEntryIterator();
}
public final boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry,?> e = (Map.Entry,?>) o;
Object key = e.getKey();
Node candidate = getNode(hash(key), key);
return candidate != null && candidate.equals(e);
}
public final boolean remove(Object o) {
if (o instanceof Map.Entry) {
Map.Entry,?> e = (Map.Entry,?>) o;
Object key = e.getKey();
Object value = e.getValue();
return removeNode(hash(key), key, value, true, true) != null;
}
return false;
}
public final Spliterator> spliterator() {
return Spliterators.spliterator(this, Spliterator.SIZED |
Spliterator.ORDERED |
Spliterator.DISTINCT);
}
public final void forEach(Consumer super Map.Entry> action) {
if (action == null)
throw new NullPointerException();
int mc = modCount;
for (LinkedHashMap.Entry e = head; e != null; e = e.after)
action.accept(e);
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
//å°é®å¼å¯¹ä¾æ¬¡ä¼ éç»å½æ° apply
public void forEach(BiConsumer super K, ? super V> action) {
if (action == null)
throw new NullPointerException();
int mc = modCount;
for (LinkedHashMap.Entry e = head; e != null; e = e.after)
action.accept(e.key, e.value);
if (modCount != mc)
throw new ConcurrentModificationException();
}
//å°é®å¼å¯¹ä¾æ¬¡ä¼ éç»å½æ° applyï¼å¹¶ä»¥å½æ°çè¿åå¼ä½ä¸ºé®å¼å¯¹çæ°å¼
public void replaceAll(BiFunction super K, ? super V, ? extends V> function) {
if (function == null)
throw new NullPointerException();
int mc = modCount;
for (LinkedHashMap.Entry e = head; e != null; e = e.after)
e.value = function.apply(e.key, e.value);
if (modCount != mc)
throw new ConcurrentModificationException();
}
//è¿ä»£å¨
abstract class LinkedHashIterator {
//ä¸ä¸ä¸ªç»ç¹
LinkedHashMap.Entry next;
//å½åç»ç¹
LinkedHashMap.Entry current;
int expectedModCount;
LinkedHashIterator() {
next = head;
expectedModCount = modCount;
current = null;
}
//夿æ¯å¦è¿æä¸ä¸ä¸ªå
ç´
public final boolean hasNext() {
return next != null;
}
//ç¨äºè·åä¸ä¸ä¸ªç»ç¹
final LinkedHashMap.Entry nextNode() {
LinkedHashMap.Entry e = next;
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
if (e == null)
throw new NoSuchElementException();
current = e;
next = e.after;
return e;
}
//ç§»é¤ç»ç¹ current
public final void remove() {
Node p = current;
if (p == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
current = null;
K key = p.key;
removeNode(hash(key), key, null, false, false);
expectedModCount = modCount;
}
}
//ç¨äºè¿ä»£ key
final class LinkedKeyIterator extends LinkedHashIterator
implements Iterator {
public final K next() { return nextNode().getKey(); }
}
//ç¨äºè¿ä»£ value
final class LinkedValueIterator extends LinkedHashIterator
implements Iterator {
public final V next() { return nextNode().value; }
}
//ç¨äºè¿ä»£é®å¼å¯¹
final class LinkedEntryIterator extends LinkedHashIterator
implements Iterator> {
public final Map.Entry next() { return nextNode(); }
}
}