1、分析LinkedHashMap
它是HashMap的子类,可以保持元素按插入或访问有序,这与TreeMap按键排序不同
LinkedHashMap是HashMap的子类,但内部还有一个双向链表维护键值对的顺序,对每个键值对即位于哈希表中,也位于这个双向链表中。
LinkedHashMap支持两种顺序:
一种是插入顺序
;另外一种是访问顺序
按插入顺序
先添加的在前面;后添加的在跟后面,修改操作不影响顺序
按访问顺序
最末尾的是最近访问的,最开始的最久没被访问
2、实现原理(基于JDK1.7)
2.1、继承体系
//LinkedHashMap是HashMap的子类
//但内部还有一个双向链表维持键值对的顺序:每个键即位于哈希表中,也位于双向链表中
//LinkedHashMap支持两种顺序:一种是插入顺序;另外一种是访问顺序
public class LinkedHashMap<K,V>
extends HashMap<K,V>
implements Map<K,V>
LinkedhashM,ap
继承了HashMap
,在HashMap
的基础上对部分方法进行重写与加入自身的一些特性需要结合HashMap中的方法进行分析
2.2、内部组成
//表示双向链表的头,它的类型是Entry的内部类
private transient Entry<K,V> header;
//表示双向链表的头,它的类型是Entry的内部类
private transient Entry<K,V> header;
同时在内部有一个Entrt
类,这个类是HashMap.Entry的子类,增嫁了两个变量Before
和`AFTER·,指向链表中的前驱和后继
HashMap中的Entry类
//HashMap中的Entry类
static class Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
Entry<K,V> next;
int hash;
//注意这个方法在这个类为空方法,需要重写
void recordAccess(HashMap<K,V> m) {
}
//注意这个方法在这个类为空方法,需要重写
void recordRemoval(HashMap<K,V> m) {
}
}
LinkedHashMap中的Entry类
//相当于节点是链表维护了前向与后向的节点
//维护了插入的顺序
//
private static class Entry<K,V> extends HashMap.Entry<K,V> {
//增加了两个变量:before和after
Entry<K,V> before, after;
Entry(int hash, K key, V value, HashMap.Entry<K,V> next) {
super(hash, key, value, next);
}
/**
* Removes this entry from the linked list.
*/
private void remove() {
before.after = after;
after.before = before;
}
/**
* Inserts this entry before the specified existing entry in the list.
*/
private void addBefore(Entry<K,V> existingEntry) {
after = existingEntry;
before = existingEntry.before;
before.after = this;
after.before = this;
}
/**
* This method is invoked by the superclass whenever the value
* of a pre-existing entry is read by Map.get or modified by Map.set.
* If the enclosing Map is access-ordered, it moves the entry
* to the end of the list; otherwise, it does nothing.
*/
//record和recordacess是HashMap.Entry中定义的方法
//在HashMap中这两个方法实现为空
//如果是按访问顺序,则将还节点移到链表的尾部
void recordAccess(HashMap<K,V> m) {
LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
//如果按照访问有序,recordAceess调整节点到链表末尾
if (lm.accessOrder) {
lm.modCount++;
remove();
addBefore(lm.header);
}
}
//将该节点从链表中删除
void recordRemoval(HashMap<K,V> m) {
remove();
}
}
2.3、构造方法
在HashMap
中构造方法中有一个init
方法,其为空函数。
HashMap中的构造函数j举例:
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
threshold = initialCapacity;
init();
} public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
threshold = initialCapacity;
//HashMap中init()函数为空
init();
}
void init() {
}
在LinkedHashMap中重写了init
方法,用于初始化链表的头节点
//LinkedHashMap重写的init方法
//在HashMap的构造方法中,会调用init方法,init方法在HashMap的实现中为空
//也就是被设计用来重写的,
//在LinkedHashSet中用于初始化链表的头节点
@Override
void init() {
//header被初始化为一个Entry对象
header = new Entry<>(-1, null, null, null);
//前驱和后继都指向自己
header.before = header.after = header;
}
//LinkedHashMap构造函数举例
public LinkedHashMap(int initialCapacity, float loadFactor) {
//调用HashMap中的方法
super(initialCapacity, loadFactor);
accessOrder = false;
}
public LinkedHashMap() {
super();
accessOrder = false;
}
public LinkedHashMap() {
super();
accessOrder = false;
}
public LinkedHashMap(int initialCapacity,
float loadFactor,
boolean accessOrder) {
super(initialCapacity, loadFactor);
this.accessOrder = accessOrder;
}
2.4、put方法
LinkedHashMap
是继承自hASHmAP
的,它没有对其进行重写但是对其中调用的一些方法进行了重写,从而实现了功能的增加。
HashMap中的put方法
public V put(K key, V value) {
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
if (key == null)
return putForNullKey(value);
int hash = hash(key);
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
//添加节点
addEntry(hash, key, value, i);
return null;
}
void addEntry(int hash, K key, V value, int bucketIndex) {
//考虑扩容
if ((size >= threshold) && (null != table[bucketIndex])) {
resize(2 * table.length);
hash = (null != key) ? hash(key) : 0;
bucketIndex = indexFor(hash, table.length);
}
createEntry(hash, key, value, bucketIndex);
}
//添加节点
void createEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V> e = table[bucketIndex];
table[bucketIndex] = new Entry<>(hash, key, value, e);
size++;
}
LinkedHashMap中的放
LinkedHashMap
重写了createEntrt
和addEntry
方法,从而实现了填上附加的一些功能。
重写的addEntry方法
//如果是新建则会调用addEntry方法添加节点
void addEntry(int hash, K key, V value, int bucketIndex) {
//调用父类的addEntry;父类的addEntry会调用createEntry创建爱你节点,,
//LinkedHashMap重写了createEntry方
super.addEntry(hash, key, value, bucketIndex);
//添加完成后,调用removeEldestEntrt检查是否应该删除老街店
Entry<K,V> eldest = header.after;
//如果返回值为true,则调用removeEntryForKey(eldest.key);
if (removeEldestEntry(eldest)) {
//删除节点时会调用recordRemoval方法,该方法被LinkedHashMap.Entry重写了,会将节点从链表中删除
removeEntryForKey(eldest.key);
}
}
//重写的createEntry方法
void createEntry(int hash, K key, V value, int bucketIndex) {
HashMap.Entry<K,V> old = table[bucketIndex];
//新建节点,并加入到哈希表中,同时加入链表中
Entry<K,V> e = new Entry<>(hash, key, value, old);
//新建节点,并加入到哈希表中,同时加入链表中
table[bucketIndex] = e;
//新建节点,并加入到哈希表中,同时加入链表中
e.addBefore(header);
size++;
}
/**
* Inserts this entry before the specified existing entry in the list.
*/
private void addBefore(Entry<K,V> existingEntry) {
after = existingEntry;
before = existingEntry.before;
before.after = this;
after.before = this;
}
2.5、get方法
HashMap中的实现
//HashMap中的get方法
public V get(Object key) {
if (key == null)
return getForNullKey();
Entry<K,V> entry = getEntry(key);
return null == entry ? null : entry.getValue();
}
LinkedHashMap中的时效内
//与HashMap相比,主要是调用了节点的recordAcess方法,如果按访问有序,
//recordAceess调整该节点到末尾末尾
public V get(Object key) {
//与hashMap一致
Entry<K,V> e = (Entry<K,V>)getEntry(key);
if (e == null)
return null;
//recordAceess调整该节点到链表末尾
e.recordAccess(this);
return e.value;
}
//record和recordacess是HashMap.Entry中定义的方法
//在HashMap中这两个方法实现为空
//如果是按访问顺序,则将还节点移到链表的尾部
void recordAccess(HashMap<K,V> m) {
LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
//如果按照访问有序,recordAceess调整节点到链表末尾
if (lm.accessOrder) {
lm.modCount++;
remove();
addBefore(lm.header);
}
2.6、containsValue
//HashMap中的方法:复杂度O(N^2)
public boolean containsValue(Object value) {
if (value == null)
return containsNullValue();
Entry[] tab = table;
for (int i = 0; i < tab.length ; i++)
for (Entry e = tab[i] ; e != null ; e = e.next)
if (value.equals(e.value))
return true;
return false;
}
//LinkedHashSet中的犯法 复杂度O(N)
public boolean containsValue(Object value) {
//遍历链
// Overridden to take advantage of faster iterator
if (value==null) {
for (Entry e = header.after; e != header; e = e.after)
if (e.value==null)
return true;
} else {
for (Entry e = header.after; e != header; e = e.after)
if (value.equals(e.value))
return true;
}
return false;
}