我们知道我们遍历HashMap的时候,并不是按照插入顺序取出的。而是按照计算的hash然后再去计算应该存放的桶的哪一个位置。即是按照桶的顺序来遍历的,如果在同一个位置,然后接着去按照链表的顺序……
如果我们希望实现按照插入顺序来遍历,怎么办呢,LinkedHashMap就派上用场了。
那么,LinkedHashMap具有哪些特性呢?
首先:可以按照元素的添加顺序进行迭代;
其次:可以根据元素的访问顺序获取值
最后:支持删除一些老的key,但是需要自己实现,它本身只是简单的返回false.
它又是如何使得我们可以按照元素添加顺序或者元素访问顺序迭代呢?
是因为它在维持一个双向链表,可以把所有添加的Entry都维护在这个双向链表中。headentry after永远指向第一个添加的entry,添加的第一个entry before指向headentry。 然后每一次新加的entry 的 after都指向head,head的before 指向 这个新加的;然后新加的entrybefore指向他之前的那一个entry,他之前的那个entry after指向新加的这个entry.
如图示:
所以一般来说插在后面的元素,按照顺序插入这个双向的循环链表,来维护一个顺序。
一 比较重要的属性
除了继承HashMap的某些属性之外,还涉及到自己的一些属性。
//这个属性很重要,它是这个双向链表的头,初始化的时候header.
before=header.after=header;都是指向自己。
privatetransient Entry<K,V>header;
//是否按照map的访问顺序动态的改变元素的双向链表中位置
privatefinal booleanaccessOrder;
二构造方法
publicLinkedHashMap(int initialCapacity) {
super(initialCapacity);
accessOrder = false;
}
publicLinkedHashMap(int initialCapacity,floatloadFactor, boolean accessOrder) {
super(initialCapacity,loadFactor);
this.accessOrder= accessOrder;
}
它的构造方法都是基于HashMap的,但是至于能不能按照访问顺序获取数据,默认是false.
三重要的方法
void addEntry(inthash, K key,V value, intbucketIndex) {
createEntry(hash,key, value,bucketIndex);
Entry<K,V> eldest = header.after;
if(removeEldestEntry(eldest)) {
removeEntryForKey(eldest.key);
} else {
if (size>= threshold)
resize(2 *table.length);
}
}
重载了父类addEntry方法,首先创建entry,如果需要删除最老的数据,就把第一个数据给删掉,因为header.after始终指向的是链表第一个数据。
void createEntry(inthash, K key,V value, intbucketIndex) {
HashMap.Entry<K,V>old = table[bucketIndex];
Entry<K,V> e =new Entry<K,V>(hash,key, value, old);
table[bucketIndex] = e;
e.addBefore(header);
size++;
}
产生新的Entry,放入桶中,如果i相同,则放入单向链表。
e.addBefore开始构建双向链表。
privatevoid addBefore(Entry<K,V>existingEntry) {
after = existingEntry;
before = existingEntry.before;
before.after = this;
after.before = this;
}
如图示:
publicV get(Object key) {
Entry<K,V> e =(Entry<K,V>)getEntry(key);
if (e==null)
return null;
e.recordAccess(this);
return e.value;
}
//recordAccess方法就是,只要你访问了某个enrty,我就把这个entry删掉,然后放到第一个位置上,然后下次访问的时候,这个就是第一个
void recordAccess(HashMap<K,V>m){
LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
if (lm.accessOrder){
lm.modCount++;
remove();
addBefore(lm.header);
}
}
在遍历的时候,这个类很重要:
privateabstract class LinkedHashIterator<T>implements Iterator<T> {
Entry<K, V> nextEntry = header.after;
Entry<K, V> lastReturned = null;
int expectedModCount =modCount;
//我们判断有没有遍历完的依据就是nextEntry不等于 header,只要不是header就表示还有。
public boolean hasNext() {
return nextEntry !=header;
}
public void remove() {
if (lastReturned ==null)
throw new IllegalStateException();
if (modCount!= expectedModCount)
throw new ConcurrentModificationException();
LinkedHashMap.this.remove(lastReturned.key);
lastReturned =null;
expectedModCount =modCount;
}
Entry<K, V>nextEntry() {
if (modCount!= expectedModCount)
throw new ConcurrentModificationException();
if (nextEntry ==header)
throw new NoSuchElementException();
Entry<K, V>e =lastReturned =nextEntry;
nextEntry =e.after;
return e;
}
}