Java TreeMap 和 LinkedHashMap【笔记】

TreeMap

TreeMap基本结构

TreeMap 底层的数据结构就是红黑树,和 HashMap 的红黑树结构一样

与HashMap不同的是,TreeMap 利用了红黑树左节点小,右节点大的性质,根据 key 进行排序,使每个元素能够插入到红黑树大小适当的位置,维护了 key 的大小关系,适用于 key 需要排序的场景

TreeMap 常见属性

//比较器,如果外部有传进来 Comparator 比较器,首先用外部的
//如果外部比较器为空,则使用 key 自己实现的 Comparable#compareTo 方法
//比较手段和上面日常工作中的比较 demo 是一致的
private final Comparator<? super K> comparator; //红黑树的根节点
private transient Entry<K,V> root; //红黑树的已有元素大小
private transient int size = 0; //树结构变化的版本号,用于迭代过程中的快速失败场景
private transient int modCount = 0; //红黑树的节点
static final class Entry<K,V> implements Map.Entry<K,V> {}

TreeMap 新增节点

第一步,判断红黑树的节点是否为空,为空的话,新增的节点直接作为根节点

代码:

Entry<K,V> t = root;
if (t == null) {
// compare 方法限制了 key 不能为 null
compare(key, key);
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}

第二步,自旋找到key应该新增的位置,然后挂在那个节点的头上,通过 compare 来比较 key 的大小,然后根据红黑树左小右大的特性,进行判断,找到应该新增节点的父节点

代码:

Comparator<? super K> cpr = comparator;
if (cpr != null) {
do {
parent = t;
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}

第三步,在父节点的左边或右边插入新增节点

代码:

if (cmp < 0)
parent.left = e;
else
parent.right = e;

第四步,着色旋转,使红黑树达到平衡,结束

我们可以发现,在新增节点时,利用了红黑树左小右大的特性,从根节点不断往下查找,直到找到节点是 null 为止,在查找的过程中,发现 key 值已经存在的话,就直接覆盖,且TreeMap 是禁止 key 是 null 值的

LinkedHashMap(没咋理解)

LinkedHashMap基础结构

LinkedHashMap 本身是继承 HashMap 的,所以它拥有 HashMap 的所有特性,再此基础上,还提供了两大特性

第一大特性,按照插入顺序进行访问

链表特性

LinkedHashMap 的数据结构,怎么说呢,就像是把 LinkedList 的每个元素换成了 HashMap 的 Node,LinkedHashMap 像是两者的结合体,不过也正是因为增加了这些结构,才能把 Map 的元素都串联起来,形成一个链表,而既然是个链表,那就可以保证顺序了

按照顺序新增

在LinkedHashMap 初始化时,我们默认 accessOrder 为 false,意思就是会按照插入顺序提供访问,插入方法使用的是父类 HashMap 的 put 方法,不过覆写了 put 方法执行中调用的 newNode以及newTreeNode 和 afterNodeAccess 方法,put 方法中的newNode以及newTreeNode 方法,可以控制新增节点追加到链表的尾部,这样每次新节点都追加到尾部,即可保证插入顺序了

按照顺序访问

LinkedHashMap 只提供了单向访问,即按照插入的顺序从头到尾进行访问,不能像 LinkedList 那样可以做到双向访问,因此主要通过迭代器进行访问,在迭代器初始化的时候,默认从头节点开始访问,在迭代的过程中,不断访问当前节点的 after 节点即可

Map 对 key、value 和 entity 都提供出了迭代的方法,假设我们需要迭代 entity,就可使用 LinkedHashMap.entrySet().iterator() 这种写法直接返回 LinkedHashIterator ,LinkedHashIterator 是迭代器,我们调用迭代器的 nextNode 方法就可以得到下一个节点

先前在新增节点时,就已经使用put 方法中的newNode以及newTreeNode 方法来维护元素之间的插入顺序,所以迭代访问时非常简单,只需要不断的访问当前节点的下一个节点即可

第二大特性,实现了访问最少最先删除功能,其目的是把很久都没有访问的 key 自动删除。

这种策略其实就是 LRU(Least recently used,最近最少使用),简单来说,在链表中的LRU,就是经常访问的元素会被追加到队尾,这样不经常访问的数据自然就被前移,慢慢靠近队头,然后我们可以通过设置删除策略,比如当 Map 元素个数大于多少时,把头节点删除,这就实现了最少最先的删除

一些问题:

HashMap、TreeMap、LinkedHashMap 三者异同?

相同点:

1.三者在特定的情况下,都会使用红黑树;

2.底层的 hash 算法相同;

3.在迭代的过程中,如果 Map 的数据结构被改动,都会报相同的错(ConcurrentModificationException)

不同点:

数据结构

HashMap 数据结构以数组为主,查询非常快

TreeMap 数据结构以红黑树为主,利用了红黑树左小右大的特点,可以实现 key 的排序

LinkedHashMap 在 HashMap 的基础上增加了链表的结构,实现了插入顺序访问和最少访问删除两种策略

且因为数据结构不一样,三者的上层包装的 api 略有差别

应用场景

TreeMap 适合需要根据 key 进行排序的场景

LinkedHashMap 适合按照插入顺序访问,或需要删除最少访问元素的场景

剩余场景我们使用 HashMap 即可,我们工作中大部分场景基本都在使用 HashMap

LinkedHashMap 中的 LRU 是什么意思?是怎么实现的?

LRU ,英文全称:Least recently used,中文叫做最近最少访问,在 LinkedHashMap 中,也叫做最少访问删除策略

我们可以通过 removeEldestEntry 方法设定一定的策略,使最少被访问的元素,在适当的时机被删除,原理是在 put 方法执行的最后,LinkedHashMap 会去检查这种策略,如果满足策略,就删除头节点

保证头节点就是最少访问的元素的原理是:LinkedHashMap 在 get 的时候,都会把当前访问的节点,移动到链表的尾部,慢慢的,就会使头部的节点都是最少被访问的元素

Java TreeMap 和 LinkedHashMap【笔记】的更多相关文章

  1. [Java] TreeMap - 源代码学习笔记

    TreeMap 实现了 SortedMap 和 NavigableMap 接口,所有本文还会记录 SortedMap 和 NavigableMap 的阅读笔记. SortedMap 1. 排序的比较应 ...

  2. Java笔记(八)TreeMap & TreeSet & LinkedHashMap

    TreeMap & TreeSet & LinkedHashMap 一.TreeMap HashMap缺陷:键值对之间没有特定的顺序.在TreeMap中, 键值对之间按键有序,Tree ...

  3. 【Java】Map杂谈,hashcode()、equals()、HashMap、TreeMap、LinkedHashMap、ConcurrentHashMap

    参考的优秀文章: <Java编程思想>第四版 <Effective Java>第二版 Map接口是映射表的结构,维护键对象与值对象的对应关系,称键值对. > hashco ...

  4. Java性能调优笔记

    Java性能调优笔记 调优步骤:衡量系统现状.设定调优目标.寻找性能瓶颈.性能调优.衡量是否到达目标(如果未到达目标,需重新寻找性能瓶颈).性能调优结束. 寻找性能瓶颈 性能瓶颈的表象:资源消耗过多. ...

  5. 从头认识java-15.7 Map(7)-TreeMap与LinkedHashMap

    这一章节我们来讨论一下Map两个比較经常使用的实现:TreeMap与LinkedHashMap. 1.TreeMap 特性:依照key来排序 package com.ray.ch14; import ...

  6. JAVA编程思想读书笔记(二)--容器

    接上篇JAVA编程思想读书笔记(一) 第八章.对象的容纳 No1: java提供了四种类型的集合类:Vector(矢量).BitSet(位集).Stack(堆栈).Hashtable(散列表) No2 ...

  7. Java二次复习笔记(1)

    Java二次复习笔记(1) Java采用的默认字符编码集是Unicode. byte=byte+byte报错,值为int,而byte+=byte不报错.同样short = short+short报错, ...

  8. JAVA GUI编程学习笔记目录

    2014年暑假JAVA GUI编程学习笔记目录 1.JAVA之GUI编程概述 2.JAVA之GUI编程布局 3.JAVA之GUI编程Frame窗口 4.JAVA之GUI编程事件监听机制 5.JAVA之 ...

  9. Java编程思想重点笔记(Java开发必看)

    Java编程思想重点笔记(Java开发必看)   Java编程思想,Java学习必读经典,不管是初学者还是大牛都值得一读,这里总结书中的重点知识,这些知识不仅经常出现在各大知名公司的笔试面试过程中,而 ...

随机推荐

  1. Java Set HashSet

    import java.util.HashSet; import java.util.Set; /** Set存储特点:数据无序.不可重复 Set接口的实现类: HashSet:Set接口的主要实现类 ...

  2. SpringBoot:SpringCloud与SpringBoot兼容版本参(其它组件兼容情况)

    SpringCloud --- Springboot 版本兼容 SpringCloud SpringBoot Edgware.SR5 >=1.5.0.RELEASE and <=1.5.2 ...

  3. nginx的基本使用

    下载: https://nginx.org/en/download.html  Window下安装: 下载好了之后直接解压就行了.(解压目录切记别含有中文) 启动:1️⃣直接双击nginx.exe2️ ...

  4. GO系列-ini文件处理

    gopkg.in/ini.v1 配置加载 创建一个空的配置 cfg := ini.Empty() 直接加载存在的配置文件,如果文件不存在就会报错 cfg, err := ini.Load(" ...

  5. 没事就要多做多练,Shell脚本循环例题做一做

    Shell脚本循环例题                 一.示例1                 二.示例2                 三.示例3                 四.示例4 ...

  6. Serverless与Web后端天生不合?

    Serverless/Faas/BaaS 等概念在这几年的技术圈中是绝对的热点词汇之一,国内外众多云厂商也纷纷推出自家的 Serverless 和函数计算产品,微信也依托腾讯云推出了基于 Server ...

  7. Java中为什么notify()可能导致死锁,而notifyAll()则不会(针对生产者-消费者模式)

    1.先说两个概念:锁池 和 等待池 锁池:假设线程A已经拥有了某个对象(注意:不是类)的锁,而其它的线程想要调用这个对象的某个synchronized方法(或者synchronized块),由于这些线 ...

  8. vs2013:asp.net网站发布

    1."生成"菜单"生成网站" 2."发布网站" 3.配置文件--自定义,名称 4.发布方法:文件系统,确定目标位置(另外的) 5.配置选择r ...

  9. Java程序设计当堂测试感受

    开学第一周的周四,按照王主任的安排,进行了Java当堂测试,来检测暑假八周的学习成果.这一堂课真是让我哭笑不得,这一节课三个小时都在写代码,感觉暑假学的一点点代码什么都不是,写一个系统都完不成,感觉自 ...

  10. WinForm使用DataGridView实现类似Excel表格的查找替换

    在桌面程序开发过程中我们常常使用DataGridView作为数据展示的表格,在表格中我们可能要对数据进行查找或者替换. 其实要实现这个查找替换的功能并不难,记录下实现过程,不一定是最好的方式,但它有用 ...