Java TreeMap 和 LinkedHashMap【笔记】

TreeMap

TreeMap基本结构

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

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

TreeMap 常见属性

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

TreeMap 新增节点

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

代码:

  1. Entry<K,V> t = root;
  2. if (t == null) {
  3. // compare 方法限制了 key 不能为 null
  4. compare(key, key);
  5. root = new Entry<>(key, value, null);
  6. size = 1;
  7. modCount++;
  8. return null;
  9. }

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

代码:

  1. Comparator<? super K> cpr = comparator;
  2. if (cpr != null) {
  3. do {
  4. parent = t;
  5. cmp = cpr.compare(key, t.key);
  6. if (cmp < 0)
  7. t = t.left;
  8. else if (cmp > 0)
  9. t = t.right;
  10. else
  11. return t.setValue(value);
  12. } while (t != null);
  13. }

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

代码:

  1. if (cmp < 0)
  2. parent.left = e;
  3. else
  4. 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. 解决数据库连接池连接mysql时,每隔8小时mysql自动断开所有连接的问题

    解决数据库连接池连接mysql时,每隔8小时mysql自动断开所有连接的问题 最近有个问题非常讨厌,我们的工程中使用自己的连接池连接mysql数据库,可mysql数据库每隔8小时就会自动断开所有链接, ...

  2. php操作redis集群哨兵模式

    前段时间项目里正好用到了redis的集群哨兵部署,因为此前并无了解过,所以一脸懵逼啊,查阅了几篇资料,特此综合总结一下,作为记录. 写在前沿:随着项目的扩张,对redis的依赖也越来越大,为了增强re ...

  3. 构建自己的dockerfile

    一)创建自己的CentOS 构建dockerfile的原理图 Docker Hub中99%的镜像都是从FROM scratch这个基础镜像过来的.然后配置需要的软件和配置来进行构建. FROM #基础 ...

  4. python操作elasticsearch增、删、改、查

    最近接触了个新东西--es数据库 这东西虽然被用的很多,但我是前些天刚刚接触的,发现其资料不多,学起来极其痛苦,写个文章记录下 导入库from elasticsearch import Elastic ...

  5. windows程序快速启动的方式:WIN键+R

    WIN键+R是windows快速启动程序的一种方式,一般能独立运行的程序都能以这种方式启动.如notepad.calc.explorer等程序. 在命令行方式下explorer加上不同的参数,会得到不 ...

  6. springboot-3-web开发

    一.视图层技术thymeleaf 我们一般都是基于3.x版本 1.流程: 导入依赖 <!--整合thymeleaf技术--> <dependency> <groupId& ...

  7. php使用curl模拟post请求

    废话不多说,直接上代码,做个笔记. $url="http://localhost/header_server.php"; $body = array("mobile&qu ...

  8. python通过sha1和base64生成签名

    一.python生成时间戳 # 案例 生成时间戳 import time t = time.time() print(t) # 原始时间数据 print(int(t)) # 秒级时间戳 print(i ...

  9. Odoo的附件大小限制

    Odoo使用binary类型来保存附件数据,可以直接支持附件数据的上传.但是在实际使用中,有可能遇到附件文件大小超过限制的情况,如下图: 但是ERP定制过程中难免会遇到客户确实需要上传超大附件,那么怎 ...

  10. 第七篇 -- photoshop cs6 激活

    下载photoshop cs6破解版 下载amtlib.dll 破解就是将amtlib.dll替换,路径:C:\Program Files\Adobe\Adobe Photoshop CS6 (64 ...