TreeMap & TreeSet & LinkedHashMap

一、TreeMap

HashMap缺陷:键值对之间没有特定的顺序。在TreeMap中,

键值对之间按键有序,TreeMap的实现基础是排序二叉树。

一)基本用法

构造方法:

  1. //无参构造方法要求Map中的键实现Compareble接口
  2. public TreeMap()
  3. //如果comparator不为null,在TreeMap内部进行比较时会调用compare方法
  4. public TreeMap(Comparator<? super K> comparator)

TreeMap按键的比较结果对键进行重排,即使键实际上不同,但只要比较

结果相同,它们就会被认为相同,键只会保留一份。

  1. TreeMap<String, String> map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
  2. map.put("t", "try");
  3. map.put("T", "order");
  4. for (Map.Entry<String, String> kv : map.entrySet()) {
  5. System.out.println(kv.getKey() + " = " + kv.getValue()); //t = order
  6. }

二)实现原理

TreeMap内部是用红黑树实现的,红黑树是一种大致平衡的排序二叉树。

1)内部组成 

内部主要成员:

  1. private final Comparator<? super K> comparator;
  2. private transient Entry<K,V> root = null; //指向二叉树根节点
  3. private transient int size = 0;

内部类Entry:

  1. static final class Entry<K,V> implements Map.Entry<K,V> {
  2. K key;
  3. V value;
  4. Entry<K,V> left = null;
  5. Entry<K,V> right = null;
  6. Entry<K,V> parent;
  7. boolean color = BLACK; //表示节点颜色,非黑即红。
  8. Entry(K key, V value, Entry<K,V> parent) {
  9. this.key = key;
  10. this.value = value;
  11. this.parent = parent;
  12. }
  13. }

2)保存键值对 

put方法代码,添加第一个节点的情况:

  1. public V put(K key, V value) {
  2. Entry<K,V> t = root;
  3. if(t == null) {
  4. compare(key, key); // type (and possibly null) check
  5. root = new Entry<>(key, value, null);
  6. size = 1;
  7. modCount++;
  8. return null;
  9. }
  10. //…

如果不是第一次添加,寻找父节点,寻找父节点根据是否设置了comparator分为两种情况:

  1. int cmp;
  2. Entry<K,V> parent;
  3. //split comparator and comparable paths
  4. Comparator<? super K> cpr = comparator;
  5. if(cpr != null) {
  6. do {
  7. parent = t;
  8. cmp = cpr.compare(key, t.key);
  9. if(cmp < 0)
  10. t = t.left;
  11. else if(cmp > 0)
  12. t = t.right;
  13. else
  14. return t.setValue(value);
  15. } while (t != null);
  16. }
  1. else {
  2. if(key == null)
  3. throw new NullPointerException();
  4. Comparable<? super K> k = (Comparable<? super K>) key;
  5. do {
  6. parent = t;
  7. cmp = k.compareTo(t.key);
  8. if(cmp < 0)
  9. t = t.left;
  10. else if(cmp > 0)
  11. t = t.right;
  12. else
  13. return t.setValue(value);
  14. } while(t != null);
  15. }

基本思路:循环比较找到父节点,并插入作为其左孩子或者右孩子,然后调整保持树的大致平衡。

3)根据键获取值 

  1. public V get(Object key) {
  2. Entry<K,V> p = getEntry(key);
  3. return(p==null ? null : p.value);
  4. }
  1. final Entry<K,V> getEntry(Object key) {
  2. // Offload comparator-based version for sake of performance
  3. if(comparator != null)
  4. return getEntryUsingComparator(key);
  5. if(key == null)
  6. throw new NullPointerException();
  7. Comparable<? super K> k = (Comparable<? super K>) key;
  8. Entry<K,V> p = root;
  9. while(p != null) {
  10. int cmp = k.compareTo(p.key);
  11. if(cmp < 0)
  12. p = p.left;
  13. else if(cmp > 0)
  14. p = p.right;
  15. else
  16. return p;
  17. }
  18. return null;
  19. }

4)查看是否包含某个值 

按值查找需要遍历

  1. public boolean containsValue(Object value) {
  2. for(Entry<K,V> e = getFirstEntry(); e != null; e = successor(e))
  3. if(valEquals(value, e.value))
  4. return true;
  5. return false;
  6. }
  1. final Entry<K,V> getFirstEntry() {
  2. Entry<K,V> p = root;
  3. if(p != null)
  4. while (p.left != null)
  5. p = p.left;
  6. return p;
  7. }
  1. static <K,V> TreeMap.Entry<K,V> successor(Entry<K,V> t) {
  2. if(t == null)
  3. return null;
  4. else if(t.right != null) {
  5. Entry<K,V> p = t.right;
  6. while (p.left != null)
  7. p = p.left;
  8. return p;
  9. } else {
  10. Entry<K,V> p = t.parent;
  11. Entry<K,V> ch = t;
  12. while(p != null && ch == p.right) {
  13. ch = p;
  14. p = p.parent;
  15. }
  16. return p;
  17. }
  18. }

三)小结

ThreeMap根据键保存、查找、删除的效率比较高,为O(h),h为树的高度。

不要求排序优先考虑HashMap。

二、TreeSet

一)基本用法

TreeSet实现了两点:排重和有序

构造函数:

  1. public TreeSet()
  2. public TreeSet(Comparator<? super E> comparator)

二)实现原理

TreeSet是基于TreeMap实现的:

  1. private transient NavigableMap<E,Object> m; //背后的TreeMap
  2. private static final Object PRESENT = new Object(); //固定值
  1. TreeSet(NavigableMap<E,Object> m) {
  2. this.m = m;
  3. }
  4. public TreeSet() {
  5. this(new TreeMap<E,Object>());
  6. }

三)小结

没有重复元素,通过TreeMap实现。

三、LinkedHashMap

LinkedHashMap是HashMap的子类,可以保持元素按插入或者访问有序。

一)基本用法

该类内部有一个双向链表维护键值对顺序,每个键值对既位于哈希表中,也位于双向链表中。

该类支持两种顺序:

1)插入顺序:先添加的在前面,后添加的在后面,修改不影响顺序。

2)访问顺序:所谓访问就是get/put操作,对一个键执行get/put操作后,

其对应的键值会移到链表末尾。

LinkedHashMap有5个构造方法,其中4个都是按插入顺序,只有一个构造

方法可以指定按访问顺序:

  1. public LinkedHashMap(int initialCapacity, float loadFactor,
  2. boolean accessOrder) //accessOrder为ture就是按顺序访问

默认情况下LinkedHashMap是按插入有序的:

  1. LinkedHashMap<String, Integer> map = new LinkedHashMap<>();
  2. map.put("c", 56);
  3. map.put("d", 22);
  4. map.put("a", 33);
  5. for (Map.Entry<String, Integer> entry : map.entrySet()) {
  6. System.out.println(entry.getKey() + " = " + entry.getValue());
  7. }
  8. /*c = 56
  9. d = 22
  10. a = 33*/

插入有序一种常见的使用场景:希望Map按键有序,键在添加前已经排好序,

此时就没必要使用开销大的TreeMap。

  1. LinkedHashMap<String, Integer> map = new LinkedHashMap<>(16, 0.75f, true);
  2. map.put("c", 56);
  3. map.put("d", 22);
  4. map.put("a", 33);
  5. map.get("c");
  6. map.put("d", 66);
  7. for (Map.Entry<String, Integer> entry : map.entrySet()) {
  8. System.out.println(entry.getKey() + " = " + entry.getValue());
  9. }
  10. /*a = 33
  11. c = 56
  12. d = 66*/

二、实现原理

该类内部实例变量:

  1. private transient Entry<K,V> header; //双向链表的表头
  2. private final boolean accessOrder; //是否按访问顺序

Entry内部类:

  1. private static class Entry<K,V> extends HashMap.Entry<K,V> {
  2. Entry<K,V> before, after;
  3. Entry(int hash, K key, V value, HashMap.Entry<K,V> next) {
  4. super(hash, key, value, next);
  5. }
  6. private void remove() {
  7. before.after = after;
  8. after.before = before;
  9. }
  10. private void addBefore(Entry<K,V> existingEntry) {
  11. after = existingEntry;
  12. before = existingEntry.before;
  13. before.after = this;
  14. after.before = this;
  15. }
  16. void recordAccess(HashMap<K,V> m) {
  17. LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
  18. if(lm.accessOrder) {
  19. lm.modCount++;
  20. remove();
  21. addBefore(lm.header);
  22. }
  23. }
  24. void recordRemoval(HashMap<K,V> m) {
  25. remove();
  26. }
  27. }

init用于初始化链表的头节点:

  1. void init() {
  2. header = new Entry<>(-1, null, null, null);
  3. header.before = header.after = header;
  4. }

在LinkedHashMap中,put方法还会将节点加入到链表中来,如果是

按访问有序的,还会调整节点到末尾,并根据情况删除掉最久没有被访问的节点。

HashMap的put实现中,如果是新的键,会调用addEntry方法添加节点,LinkedHashMap重写了该方法:

  1. void addEntry(int hash, K key, V value, int bucketIndex) {
  2. super.addEntry(hash, key, value, bucketIndex);
  3. //Remove eldest entry if instructed
  4. Entry<K,V> eldest = header.after;
  5. if(removeEldestEntry(eldest)) {
  6. removeEntryForKey(eldest.key);
  7. }
  8. }

他先调用父类的addEntry方法,父类的addEntry会调用createEntry创建节点,

LinkedHashMap重写了createEntry方法:

  1. void createEntry(int hash, K key, V value, int bucketIndex) {
  2. HashMap.Entry<K,V> old = table[bucketIndex];
  3. Entry<K,V> e = new Entry<>(hash, key, value, old);
  4. table[bucketIndex] = e;
  5. //新建的节点,加入到链表末尾
  6. e.addBefore(header);
  7. size++;
  8. }

例如执行:

  1. Map<String,Integer> countMap = new LinkedHashMap<>();
  2. countMap.put("hello", 1);

执行后内存结构:

在HashMap的put实现中,如果键已经存在,则会调用节点的recordAccess方法。

LinkedHashMap.Entry重写了该方法,如果是有序访问,则调整该节点到链表末尾。

Java笔记(八)TreeMap & TreeSet & LinkedHashMap的更多相关文章

  1. Java笔记(八)……数组

    数组的概念 同一种类型数据的集合.其实数组就是一个容器. 数组的好处 可以自动给数组中的元素从0开始编号,方便操作这些元素. 数组的格式 元素类型[] 数组名 = new 元素类型[个数]; int[ ...

  2. Java TreeMap 和 LinkedHashMap【笔记】

    Java TreeMap 和 LinkedHashMap[笔记] TreeMap TreeMap基本结构 TreeMap 底层的数据结构就是红黑树,和 HashMap 的红黑树结构一样 与HashMa ...

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

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

  4. Java IO学习笔记八:Netty入门

    作者:Grey 原文地址:Java IO学习笔记八:Netty入门 多路复用多线程方式还是有点麻烦,Netty帮我们做了封装,大大简化了编码的复杂度,接下来熟悉一下netty的基本使用. Netty+ ...

  5. Java学习笔记四:Java的八种基本数据类型

    Java的八种基本数据类型 Java语言提供了八种基本类型.六种数字类型(四个整数型,两个浮点型),一种字符类型,还有一种布尔型. Java基本类型共有八种,基本类型可以分为三类,字符类型char,布 ...

  6. Java 集合系列 17 TreeSet

    java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...

  7. java中的TreeMap如何顺序按照插入顺序排序

    java中的TreeMap如何顺序按照插入顺序排序 你可以使用LinkedHashMap  这个是可以记住插入顺序的. 用LinkedHashMap吧.它内部有一个链表,保持插入的顺序.迭代的时候,也 ...

  8. Java提高十七:TreeSet 深入分析

    前一篇我们分析了TreeMap,接下来我们分析TreeSet,比较有意思的地方是,似乎有Map和Set的地方,Set几乎都成了Map的一个马甲.此话怎讲呢?在前面一篇讨论HashMap和HashSet ...

  9. Java笔记(十六)并发容器

    并发容器 一.写时复制的List和Set CopyOnWrite即写时复制,或称写时拷贝,是解决并发问题的一种重要思路. 一)CopyOnWriteArrayList 该类实现了List接口,它的用法 ...

随机推荐

  1. CentOS7+mysql(yum)

    1.现在centos上默认是没有yum源的,yum安装的是 MariaDB.所以我们需要自己先配置yum源.配置yum源步骤如下: 下载yum源:wget 'https://dev.mysql.com ...

  2. Linux文件系统及文件类型

    Linux文件系统: 根文件系统(rootfs) root filesystem LSB, FHS: (FileSystem... /etc,  /usr,  /var,  /root.... /bo ...

  3. 目标检测算法之YOLOv1与v2

    YOLO:You Only Look Once(只需看一眼) 基于深度学习方法的一个特点就是实现端到端的检测,相对于其他目标检测与识别方法(如Fast R-CNN)将目标识别任务分成目标区域预测和类别 ...

  4. IOU和非极大值抑制

    如何判断对象检测算法运作良好呢? 一.交并比(Intersection over union,IoU) 是产生的候选框(candidate bound)与原标记框(ground truth bound ...

  5. mybatis 遍历map;

    mybatis 遍历map; 参考http://blog.csdn.net/hj7jay/article/details/78652050 ps: ${m[key]}这是显示 打印的key读value ...

  6. robocopy的用法,数据库局域网备份

    robocopy,我主要是用来进行局域网数据库备份使用,不得不说这个小工具速度还是蛮快的,同时属于系统内置功能,用着还算方便. 这项功能就是RoboCopy,它是一个命令行的目录复制命令,自从Wind ...

  7. SQLServer索引及统计信息

    索引除了提高性能,还能维护数据库. 索引是一种存储结构,主要以B-Tree形式存储信息. B-Tree的定义: 1.每个节点最多只有m个节点(m>=2) 2.除了根节点和叶子节点外的每个节点上最 ...

  8. error: not found: value sqlContext/import sqlContext.implicits._/error: not found: value sqlContext /import sqlContext.sql/Caused by: java.net.ConnectException: Connection refused

    1.今天启动启动spark的spark-shell命令的时候报下面的错误,百度了很多,也没解决问题,最后想着是不是没有启动hadoop集群的问题 ,可是之前启动spark-shell命令是不用启动ha ...

  9. 实现虚拟机VMware上Centos操作系统与主机windows之间互相复制与粘贴

    1.启动你的虚拟机,然后点击虚拟机,如下所示(未安装的话,显示的是安装VMware Tools): 2.点击安装Vmware tools以后显示如下所示: 3.VMwareTools-9.9.2-24 ...

  10. Bootstraptable源码

    // @author 文志新 http://jsfiddle.net/wenyi/47nz7ez9/3/ /**关于插件的通用构造 * * 构造函数PluginName($trigger,option ...