先来说说它的特点,然后在一一通过分析源码来验证其实现原理
1、能够保证插入元素的顺序。深入一点讲,有两种迭代元素的方式,一种是按照插入元素时的顺序迭代,比如,插入A,B,C,那么迭代也是A,B,C,另一种是按照访问顺序,比如,在迭代前,访问了B,那么迭代的顺序就是A,C,B,比如在迭代前,访问了B,接着又访问了A,那么迭代顺序为C,B,A,比如,在迭代前访问了B,接着又访问了B,然后在访问了A,迭代顺序还是C,B,A。要说明的意思就是不是近期访问的次数最多,就放最后面迭代,而是看迭代前被访问的时间长短决定。
2、内部存储的元素的模型。entry是下面这样的,相比HashMap,多了两个属性,一个before,一个after。next和after有时候会指向同一个entry,有时候next指向null,而after指向entry。这个具体后面分析。
3、linkedHashMap和HashMap在存储操作上是一样的,但是LinkedHashMap多的东西是会记住在此之前插入的元素,这些元素不一定是在一个桶中,画个图。
也就是说,对于linkedHashMap的基本操作还是和HashMap一样,在其上面加了两个属性,也就是为了记录前一个插入的元素和记录后一个插入的元素。也就是只要和hashmap一样进行操作之后把这两个属性的值设置好,就OK了。注意一点,会有一个header的实体,目的是为了记录第一个插入的元素是谁,在遍历的时候能够找到第一个元素。实际上存储的样子就像上面这个图一样,这里要分清楚哦。实际上的存储方式是和hashMap一样,但是同时增加了一个新的东西就是 双向循环链表。就是因为有了这个双向循环链表,LinkedHashMap才和HashMap不一样。
4、其他一些比如如何实现的循环双向链表,插入顺序和访问顺序如何实现的就看下面的详细讲解了。
/** * LinkedHashMap entry. */ private static class Entry<K,V> extends HashMap.Entry<K,V> { // These fields comprise the doubly linked list used for iteration. //通过上面这句源码的解释,我们可以知道这两个字段,是用来给迭代时使用的,相当于一个双向链表,实际上用的时候,操作LinkedHashMap的entry和操作HashMap的Entry是一样的,只操作相同的四个属性,这两个字段是由linkedHashMap中一些方法所操作。所以LinkedHashMap的很多方法度是直接继承自HashMap。 //before:指向前一个entry元素。after:指向后一个entry元素 Entry<K,V> before, after; //使用的是HashMap的Entry构造,调用父类构造器 Entry(int hash, K key, V value, HashMap.Entry<K,V> next) { super(hash, key, value, next); } //删除当前的entry private void remove() { before.after = after; after.before = before; } //一般传进来的都是header,把当前的entry(也就是this)插入到链表的末尾,同时更新一下四个引用关系 private void addBefore(Entry<K,V> existingEntry) { after = existingEntry; before = existingEntry.before; before.after = this; after.before = this; } //在LRU算法里,一般很少访问的元素都会排在双向循环链表的靠前部分,而经常访问的元素一般都是在双向链表的靠后部分,而这些功能就是取决于该方法的。当前的entry被访问到时,我们调用此方法,先删除掉当前链表里的this(就是entry),然后再通过addBefore方法把this重新添加到链表的尾部,并且更新一下四个引用(当然这都是addBefore里的过程了),这样,访问到的元素就转移到了末尾,达到了活跃更新的状态 void recordAccess(HashMap<K,V> m) { LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m; if (lm.accessOrder) { lm.modCount++; remove(); addBefore(lm.header); } } void recordRemoval(HashMap<K,V> m) { remove(); } }
接下来就是常用方法操作了,例如put方法(该方法是继承于HashMap的):
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; //如果该entry存在,直接改值,然后将该entry移到末尾 e.recordAccess(this); return oldValue; } } modCount++; //注意,这里的addEntry方法是被LinkedHashMap覆盖了,所以调用的其实是LinkedHashMap的addEntry addEntry(hash, key, value, i); return null; }
原文地址:https://www.cnblogs.com/Booker808-java/p/9248180.html