在前面几篇博客分别介绍了这样几种集合,基于数组实现的ArrayList 类,基于链表实现的LinkedList 类,基于散列表实现的HashMap 类,本篇博客我们来介绍另一种数据类型,基于树实现的TreeSet类。

1、TreeMap 定义

  听名字就知道,TreeMap 是由Tree 和 Map 集合有关的,没错,TreeMap 是由红黑树实现的有序的 key-value 集合。

  PS:想要学懂TreeMap的实现原理,红黑树的了解是必不可少的!!!

public class TreeMap<K,V>
extends AbstractMap<K,V>
implements NavigableMap<K,V>, Cloneable, java.io.Serializable

  

  TreeMap 首先继承了 AbstractMap 抽象类,表示它具有散列表的性质,也就是由 key-value 组成。

  其次 TreeMap 实现了 NavigableMap 接口,该接口支持一系列获取指定集合的导航方法,比如获取小于指定key的集合。

  最后分别实现 Serializable 接口以及 Cloneable 接口,分别表示支持对象序列化以及对象克隆

2、字段定义

  ①、Comparator

    /**
* The comparator used to maintain order in this tree map, or
* null if it uses the natural ordering of its keys.
*
* @serial
*/
private final Comparator<? super K> comparator;

  可以看上面的英文注释,Comparator 是用来维护treemap集合中的顺序,如果为null,则按照key的自然顺序。

  Comparator 是一个接口,排序时需要实现其 compare 方法,该方法返回正数,零,负数分别代表大于,等于,小于。那么怎么使用呢?这里举个例子:

  这里有一个Person类,里面有两个属性pname,page,我们将该person对象放入ArrayList集合时,需要对其按照年龄进行排序。

 package com.ys.test;

 /**
* Create by YSOcean
*/
public class Person {
private String pname;
private Integer page; public Person() {
} public Person(String pname, Integer page) {
this.pname = pname;
this.page = page;
} public String getPname() {
return pname;
} public void setPname(String pname) {
this.pname = pname;
} public Integer getPage() {
return page;
} public void setPage(Integer page) {
this.page = page;
} @Override
public String toString() {
return "Person{" +
"pname='" + pname + '\'' +
", page=" + page +
'}';
}
}

  排序代码:

 List<Person> personList = new ArrayList<>();
personList.add(new Person("李四",20));
personList.add(new Person("张三",10));
personList.add(new Person("王五",30));
System.out.println("原始顺序为:"+personList.toString());
Collections.sort(personList, new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
//升序
//return o1.getPage()-o2.getPage();
//降序
return o2.getPage()-o1.getPage();
//不变
//return 0
}
});
System.out.println("排序后顺序为:"+personList.toString());

  打印结果为:

  

  ②、Entry

private transient Entry<K,V> root;

  对于Entry详细源码如下:

     static final class Entry<K,V> implements Map.Entry<K,V> {
K key;
V value;
Entry<K,V> left;
Entry<K,V> right;
Entry<K,V> parent;
boolean color = BLACK; /**
* Make a new cell with given key, value, and parent, and with
* {@code null} child links, and BLACK color.
*/
Entry(K key, V value, Entry<K,V> parent) {
this.key = key;
this.value = value;
this.parent = parent;
} /**
* Returns the key.
*
* @return the key
*/
public K getKey() {
return key;
} /**
* Returns the value associated with the key.
*
* @return the value associated with the key
*/
public V getValue() {
return value;
} /**
* Replaces the value currently associated with the key with the given
* value.
*
* @return the value associated with the key before this method was
* called
*/
public V setValue(V value) {
V oldValue = this.value;
this.value = value;
return oldValue;
} public boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>)o; return valEquals(key,e.getKey()) && valEquals(value,e.getValue());
} public int hashCode() {
int keyHash = (key==null ? 0 : key.hashCode());
int valueHash = (value==null ? 0 : value.hashCode());
return keyHash ^ valueHash;
} public String toString() {
return key + "=" + value;
}
}

  这里主要看 Entry 类的几个字段:

K key;
V value;
Entry<K,V> left;
Entry<K,V> right;
Entry<K,V> parent;
boolean color = BLACK;

  相信对红黑树这种数据结构了解的人,一看这几个字段就明白了,这也印证了前面所说的TreeMap底层有红黑树这种数据结构。

  ③、size

    /**
* The number of entries in the tree
*/
private transient int size = 0;

  用来表示entry的个数,也就是key-value的个数。

  ④、modCount

    /**
* The number of structural modifications to the tree.
*/
private transient int modCount = 0;

  基本上前面讲的在ArrayList,LinkedList,HashMap等线程不安全的集合都有此字段,用来实现Fail-Fast 机制,如果在迭代这些集合的过程中,有其他线程修改了这些集合,就会抛出ConcurrentModificationException异常。

  ⑤、红黑树常量

    private static final boolean RED   = false;
private static final boolean BLACK = true;

3、构造函数

  ①、无参构造函数

     public TreeMap() {
comparator = null;
}

  将比较器 comparator 置为 null,表示按照key的自然顺序进行排序。

  ②、带比较器的构造函数

     public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;
}

  需要自己实现Comparator。

  ③、构造包含指定map集合的元素

     public TreeMap(Map<? extends K, ? extends V> m) {
comparator = null;
putAll(m);
}

  使用该构造器创建的TreeMap,会默认插入m表示的集合元素,并且comparator表示按照自然顺序进行插入。

  ④、带 SortedMap的构造函数

     public TreeMap(SortedMap<K, ? extends V> m) {
comparator = m.comparator();
try {
buildFromSorted(m.size(), m.entrySet().iterator(), null, null);
} catch (java.io.IOException cannotHappen) {
} catch (ClassNotFoundException cannotHappen) {
}
}

  和上面带Map的构造函数不一样,map是无序的,而SortedMap 是有序的,使用 buildFromSorted() 方法将SortedMap集合中的元素插入到TreeMap 中。

4、添加元素

     //添加元素
public V put(K key, V value) {
TreeMap.Entry<K,V> t = root;
//如果根节点为空,即TreeMap中一个元素都没有,那么设置新添加的元素为根节点
//并且设置集合大小size=1,以及modCount+1,这是用于快速失败
if (t == null) {
compare(key, key); // type (and possibly null) check root = new TreeMap.Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
int cmp;
TreeMap.Entry<K,V> parent;
// split comparator and comparable paths
Comparator<? super K> cpr = comparator;
//如果比较器不为空,即初始化TreeMap构造函数时,有传递comparator类
//那么插入新的元素时,按照comparator实现的类进行排序
if (cpr != null) {
//通过do-while循环不断遍历树,调用比较器对key值进行比较
do {
parent = t;
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
//遇到key相等,直接将新值覆盖到原值上
return t.setValue(value);
} while (t != null);
}
//如果比较器为空,即初始化TreeMap构造函数时,没有传递comparator类
//那么插入新的元素时,按照key的自然顺序
else {
//如果key==null,直接抛出异常
//注意,上面构造TreeMap传入了Comparator,是可以允许key==null
if (key == null)
throw new NullPointerException();
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;
do {
parent = t;
cmp = k.compareTo(t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
//找到父亲节点,根据父亲节点创建一个新节点
TreeMap.Entry<K,V> e = new TreeMap.Entry<>(key, value, parent);
if (cmp < 0)
parent.left = e;
else
parent.right = e;
//修正红黑树(包括节点的左旋和右旋,具体可以看我Java数据结构和算法中对红黑树的介绍)
fixAfterInsertion(e);
size++;
modCount++;
return null;
}

  添加元素,如果初始化TreeMap构造函数时,没有传递comparator类,是不允许插入key==null的键值对的,相反,如果实现了Comparator,则可以传递key=null的键值对。

  另外,当插入一个新的元素后(除了根节点),会对TreeMap数据结构进行修正,也就是对红黑树进行修正,使其满足红黑树的几个特点,具体修正方法包括改变节点颜色,左旋,右旋等操作,这里我不做详细介绍了,具体可以参考我的这篇博客:https://www.cnblogs.com/ysocean/p/8004211.html

5、删除元素

  ①、根据key删除

     public V remove(Object key) {
//根据key找到该节点
TreeMap.Entry<K,V> p = getEntry(key);
if (p == null)
return null;
//获取该节点的value,并返回
V oldValue = p.value;
//调用deleteEntry()方法删除节点
deleteEntry(p);
return oldValue;
} private void deleteEntry(TreeMap.Entry<K,V> p) {
modCount++;
size--; //如果删除节点的左右节点都不为空,即有两个孩子
if (p.left != null && p.right != null) {
//得到该节点的中序后继节点
TreeMap.Entry<K,V> s = successor(p);
p.key = s.key;
p.value = s.value;
p = s;
} // p has 2 children // Start fixup at replacement node, if it exists.
TreeMap.Entry<K,V> replacement = (p.left != null ? p.left : p.right);
//待删除节点只有一个子节点,直接删除该节点,并用该节点的唯一子节点顶替该节点
if (replacement != null) {
// Link replacement to parent
replacement.parent = p.parent;
if (p.parent == null)
root = replacement;
else if (p == p.parent.left)
p.parent.left = replacement;
else
p.parent.right = replacement; // Null out links so they are OK to use by fixAfterDeletion.
p.left = p.right = p.parent = null; // Fix replacement
if (p.color == BLACK)
fixAfterDeletion(replacement); //TreeMap中只有待删除节点P,也就是只有一个节点,直接返回nul即可
} else if (p.parent == null) { // return if we are the only node.
root = null;
} else { // No children. Use self as phantom replacement and unlink.
//待删除节点没有子节点,即为叶子节点,直接删除即可
if (p.color == BLACK)
fixAfterDeletion(p); if (p.parent != null) {
if (p == p.parent.left)
p.parent.left = null;
else if (p == p.parent.right)
p.parent.right = null;
p.parent = null;
}
}
}

  删除节点分为四种情况:

  1、根据key没有找到该节点:也就是集合中不存在这一个节点,直接返回null即可。

  2、根据key找到节点,又分为三种情况:

    ①、待删除节点没有子节点,即为叶子节点:直接删除该节点即可。

    ②、待删除节点只有一个子节点:那么首先找到待删除节点的子节点,然后删除该节点,用其唯一子节点顶替该节点。

    ③、待删除节点有两个子节点:首先找到该节点的中序后继节点,然后把这个后继节点的内容复制给待删除节点,然后删除该中序后继节点,删除过程又转换成前面①、②两种情况了,这里主要是找到中序后继节点,相当于待删除节点的一个替身。

6、查找元素

  ①、根据key查找

     public V get(Object key) {
TreeMap.Entry<K,V> p = getEntry(key);
return (p==null ? null : p.value);
} final TreeMap.Entry<K,V> getEntry(Object key) {
// Offload comparator-based version for sake of performance
if (comparator != null)
return getEntryUsingComparator(key);
if (key == null)
throw new NullPointerException();
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;
TreeMap.Entry<K,V> p = root;
while (p != null) {
int cmp = k.compareTo(p.key);
if (cmp < 0)
p = p.left;
else if (cmp > 0)
p = p.right;
else
return p;
}
return null;
}

7、遍历元素

  通常有下面两种方法,第二种方法效率要快很多。

 TreeMap<String,Integer> map = new TreeMap<>();
map.put("A",1);
map.put("B",2);
map.put("C",3); //第一种方法
//首先利用keySet()方法得到key的集合,然后利用map.get()方法根据key得到value
Iterator<String> iterator = map.keySet().iterator();
while(iterator.hasNext()){
String key = iterator.next();
System.out.println(key+":"+map.get(key));
} //第二种方法
Iterator<Map.Entry<String,Integer>> iterator1 = map.entrySet().iterator();
while(iterator1.hasNext()){
Map.Entry<String,Integer> entry = iterator1.next();
System.out.println(entry.getKey()+":"+entry.getValue());
}

JDK1.8源码(十一)——java.util.TreeMap类的更多相关文章

  1. JDK1.8源码(五)——java.util.Vector类

    JDK1.8源码(五)--java.lang. https://www.cnblogs.com/IT-CPC/p/10897559.html

  2. JDK1.8源码(四)——java.util.Arrays类

    一.概述 1.介绍 Arrays 类是 JDK1.2 提供的一个工具类,提供处理数组的各种方法,基本上都是静态方法,能直接通过类名Arrays调用. 二.类源码 1.asList()方法 将一个泛型数 ...

  3. JDK1.8源码(四)——java.util.Arrays 类

    java.util.Arrays 类是 JDK 提供的一个工具类,用来处理数组的各种方法,而且每个方法基本上都是静态方法,能直接通过类名Arrays调用. 1.asList public static ...

  4. JDK1.8源码(七)——java.util.HashMap 类

    本篇博客我们来介绍在 JDK1.8 中 HashMap 的源码实现,这也是最常用的一个集合.但是在介绍 HashMap 之前,我们先介绍什么是 Hash表. 1.哈希表 Hash表也称为散列表,也有直 ...

  5. JDK1.8源码(五)——java.util.ArrayList 类

    关于 JDK 的集合类的整体介绍可以看这张图,本篇博客我们不系统的介绍整个集合的构造,重点是介绍 ArrayList 类是如何实现的. 1.ArrayList 定义 ArrayList 是一个用数组实 ...

  6. JDK1.8源码(六)——java.util.LinkedList 类

    上一篇博客我们介绍了List集合的一种典型实现 ArrayList,我们知道 ArrayList 是由数组构成的,本篇博客我们介绍 List 集合的另一种典型实现 LinkedList,这是一个有链表 ...

  7. JDK1.8源码(九)——java.util.LinkedHashMap 类

    前面我们介绍了 Map 集合的一种典型实现 HashMap ,关于 HashMap 的特性,我们再来复习一遍: ①.基于JDK1.8的HashMap是由数组+链表+红黑树组成,相对于早期版本的 JDK ...

  8. JDK1.8源码(十)——java.util.LinkedHashSet类

    同 HashSet 与 HashMap 的关系一样,本篇博客所介绍的 LinkedHashSet 和 LinkedHashMap 也是一致的.在 JDK 集合框架中,类似 Set 集合通常都是由对应的 ...

  9. JDK1.8源码(八)——java.util.HashSet 类

    在上一篇博客,我们介绍了 Map 集合的一种典型实现 HashMap ,在 JDK1.8 中,HashMap 是由 数组+链表+红黑树构成,相对于早期版本的 JDK HashMap 实现,新增了红黑树 ...

随机推荐

  1. html基础学习(注意点)

    浏览器会自动地在块级元素(<p><h1>)的前后添加空行 当显示页面时,浏览器会移除源代码中多余的空格和空行.所有连续的空格或空行都会被算作一个空格.需要注意的是,HTML 代 ...

  2. javaScript-进阶篇(二)

    JavaScript的内置对象 JavaScript 中的所有事物都是对象,如:字符串.数值.数组.函数等,每个对象带有属性和方法 对象的属性:反映该对象某些特定的性质的,如:字符串的长度.图像的长宽 ...

  3. C语言小程序(二)、计算第二天日期

    输入一个日期,判断下一个日期是哪一天,如果日期输入错误,则调用Linux下的cal命令显示输入的月份. #include <stdio.h> #include <stdlib.h&g ...

  4. bzoj1067降雨量

    True和False都好搞 Maybe的情况: 1.Y年和X年的降雨量已知,X年的降雨量不超过Y年的降雨量,从Y+1到X-1年中存在至少一年的降雨量未知,从Y+1到X-1年中已知的降雨量都小于X年的降 ...

  5. [BZOJ2962][清华集训]序列操作

    bzoj luogu 题意 有一个长度为\(n\) 的序列,有三个操作: \(I \ \ a\ b\ c\ :\)表示将\([a,b]\)这一段区间的元素集体增加\(c\): \(R \ \ a\ b ...

  6. CF 1036B Diagonal Walking v.2——思路

    题目:http://codeforces.com/contest/1036/problem/B 比赛时只能想出不合法的情况还有走到终点附近的方式. 设n<m,不合法就是m<k.走到终点方式 ...

  7. bzoj 4066 & bzoj 2683 简单题 —— K-D树(含重构)

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4066 https://www.lydsy.com/JudgeOnline/problem.p ...

  8. ECMAScript基本函数、概念区分总结

    1.使用Number()和parseInt() parseFloat()转换区别. 详见<JavaScript高级程序设计>P30 Number()可以针对任何类型. parseInt() ...

  9. 【转】 Pro Android学习笔记(四七):Dialog(4):一些补充和思考

    目录(?)[-] 编程思想封装接口 fragment和activity以其他fragment之间的通信 编程思想:封装接口 在小例子中,fragment会调用activity的onDialogDone ...

  10. 【转】Ruby on Rails中select使用方法

    在Ruby on Rails中真的有一堆Select helper可以用,我们经常容易混淆.常见的有三个..select, select_tag, collection_select(其余的什么sel ...