一、最基本元素存储单元

 /**
* HashMap.Node subclass for normal LinkedHashMap entries.
*/
static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before, after;
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
}
 还是原HashMap的,多了两个引用before,after,说明这是要用双向链表了

二、初始化 构造方法

public LinkedHashMap() {
super();
accessOrder = false;
}
多了 accessOrder = false; /**
* The iteration ordering method for this linked hash map: <tt>true</tt>
* for access-order, <tt>false</tt> for insertion-order.
*
* @serial
*/
final boolean accessOrder;
accessOrder表示迭代顺序,true表示访问顺序,false表示插入顺序。

三、put方法

put方法没有重写,因此和HashMap是一样的,但也有不同,不同在于LinkedHashMap实现了afterNodeAccess,afterNodeInsertion方法
看HashMap put源码

 final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e); //这个方法HashMap是空实现,这里是发生hash冲突后,找到有相同key对值进行处理时调用
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict); //这个方法HashMap也是空实现,这里是完成新数据put后调用
return null;
}
LinkedHashMap具体实现 :
1.void afterNodeAccess(Node<K,V> e) { // move node to last 注释就说明了是把该元素移到最后
LinkedHashMap.Entry<K,V> last;
if (accessOrder && (last = tail) != e) { //accessOrder用到了,默认false等于不运行,true时是按插入顺序
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;
}
}
以上代码看出在按插入顺序时,在有相同key时,对当前节点将其移到链表末尾 2.void afterNodeInsertion(boolean evict) { // possibly remove eldest
LinkedHashMap.Entry<K,V> first;
if (evict && (first = head) != null && removeEldestEntry(first)) {
K key = first.key;
removeNode(hash(key), key, null, false, true);
}
}
removeEldestEntry 方法在jdk1.8中固定返回false,因此不用关注此方法
说明一下removeElestEntry用于定义删除最老元素的规则。一旦需要删除最老节点,那么将会调用removeNode删除节点。
举个例子,如果一个链表只能维持100个元素,那么当插入了第101个元素时,以如下方式重写removeEldestEntry的话,那么将会删除最老的一个元素

 四、get方法(get进行了重写) ,在插入模式中,获取值后又一次进行把当前节点移到链表尾部操作

  有点类似LRU(最少最近使用)算法~

 /*基于访问排序*/
LinkedHashMap<String, String> linkedHashMap_accessOrder = new LinkedHashMap<>(10,0.75f,true);
linkedHashMap_accessOrder.put("name1", "josan1");
linkedHashMap_accessOrder.put("name2", "josan2");
linkedHashMap_accessOrder.put("name3", "josan3");
linkedHashMap_accessOrder.put("name4", "josan4");
linkedHashMap_accessOrder.put("name5", "josan5"); linkedHashMap_accessOrder.get("name5");
linkedHashMap_accessOrder.get("name1");
linkedHashMap_accessOrder.get("name2");
linkedHashMap_accessOrder.get("name4");
linkedHashMap_accessOrder.get("name3");
//51243顺序
for (Map.Entry<String, String> entry : linkedHashMap_accessOrder.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
System.out.println("key:" + key + ",value:" + value);
}

get方法: (accessOrder 为true表示按照访问排序),

  public V get(Object key) {
Node<K,V> e;
if ((e = getNode(hash(key), key)) == null)
return null;
if (accessOrder)
afterNodeAccess(e);
return e.value;
}

将节点移动到最后:

 void afterNodeAccess(Node<K,V> e) { // move node to last
LinkedHashMap.Entry<K,V> last;
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;
}
}

层层判断和更改引用之后,p已经独立出来了,就把p移到了map的末尾,实现了访问排序

总结

1. HashMap中TreeNode 即红黑树的节点类是继承LinkedHashMap.Entry

2. HashMap中没有使用双向链表,before, after没有使用,单纯的红黑树

3.LinkedHashMap是HashMap的子类,很多方法都是继承自父类,

LinkedHashMap中,存取等使用的也使用红黑树,但维护了before, after的链表属性,在存取时一样使用红黑树算法,

但keySet()、values()以及entrySet()等迭代时使用的是双向链表来进行的,这样的双向链表结构保证了插入顺序的有序。

4.总得来说,LinkedHashMap底层是数组加单项链表加双向链表。

5.数组加单向链表就是HashMap的结构,记录数据用,双向链表,存储插入顺序用。然后LInkedHashMap重写了两个方法,一个是添加entry,一个是创建entry,这两种时刻都要维护双向链表。

参考出处:https://www.cnblogs.com/zhaojj/p/7832680.html

最新文章

  1. android shortcut &amp;livefoulder
  2. Setup Factory 关闭正在运行的程序
  3. 【转】JSP使用上传文件,并生产高清缩略图示例
  4. 【原】iOS下KVO使用过程中的陷阱
  5. CSS三种定位机制
  6. (转)asp.net 使用cookie完成记住密码自动登录
  7. linux下挂载iso镜像文件(转)
  8. ASP.NET - 上传图片方法(单张)
  9. Jquery_基础(二) 包装集
  10. MAC下Intellij IDEA常用快捷键
  11. C# 《编写高质量代码改善建议》整理&amp;笔记 --(三)泛型&amp;委托&amp;事件
  12. Java反射通过getter和setter方法实现类的拷贝
  13. 解决libVLC无法响应鼠标消息
  14. Python笔记4——字典的一些基本操作
  15. Exception 01 : org.hibernate.engine.jndi.JndiException: Error parsing JNDI name [foo]
  16. 转:mysql where group by having
  17. centos7安装配置zabbix4.0
  18. 循环流程控制&amp;方法(3)
  19. 树莓派命令行配置连接wifi
  20. Centos6.3 下使用 Tomcat-6.0.43 非root用户 部署 生产环境 端口转发方式

热门文章

  1. Day 10:函数全局变量和局部变量及函数嵌套
  2. 001-Java命名规范
  3. CSV导入到hive中,处理分号问题
  4. 2019-7-3-Roslyn-在项目文件使用条件判断
  5. wish - 简单的窗口式(windowing) shell
  6. 关于Excel的ctrl+方向键失效的解决方法
  7. Python re标准库
  8. Java 多线程 - Volatile关键字
  9. 0704 Process继承实现多进程、Pool进程池,进程间通过队列通信,Pool实现多进程实现复制文件
  10. 二分查找总结及部分Lintcode题目分析 4