一. treeMap结构(和红黑树类似)
TreeMap中的元素默认按照keys的自然排序排列。对Integer来说,其自然排序就是数字的升序;对String来说,其自然排序就是按照字母表排序。如果是我们自定义的类型,如自定义了一个User类,就需要实现Comparabl接口,或者在构造函数中传入一个比较器。优先按照构造函数传入的比较器排序,只有在treeMap构造函数传入的比较器为空时,才用User类实现的Comparabl比较。
例子1:key是基本数据类型,可以直接比较:
import java.util.Map;
import java.util.TreeMap;
public class Demo {
public static void main(String[] args) {
Map<Integer, String> map = new TreeMap<>();
map.put(90, "sun");
map.put(57, "li");
map.put(91, "hu");
for (Map.Entry<Integer, String> data : map2.entrySet()) {
System.out.println(data.toString());//entry.toString()可以看源码的实现
}
}
}
实现效果:
57=li
90=sun
91=hu
Process finished with exit code 0
例子2:key是自定义数据类型,treemap的构造函数未传入Comparator,自定义数据类型也未实现Comparable接口。报错 cannot be cast to java.lang.Comparable,这个可以参考下面的put函数,为何会报这个异常
自定义数据类型User。
package other.collection.treemap;
import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;
public class Demo {
public static void main(String[] args) {
Map<User, String> map = new TreeMap<>();
map.put(new User(5, 90), "sun");
map.put(new User(2, 57), "li");
map.put(new User(3, 91), "hu");
for (Map.Entry<User, String> user : map.entrySet()) {
System.out.println(user.getKey().getId() + " " + user.getKey().getScore() + "=" + user.getValue());
}
}
}
class User implements Comparable<User> {
int id;
int score;
//构造函数、get()、 set()
}
执行结果:
Exception in thread "main" java.lang.ClassCastException: other.collection.treemap.User cannot be cast to java.lang.Comparable
at java.util.TreeMap.compare(TreeMap.java:1294)
at java.util.TreeMap.put(TreeMap.java:538)
at other.collection.treemap.Demo.main(Demo.java:17)
Process finished with exit code 1
3.key是自定义数据类型且实现了Comparable接口
import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;
public class Demo {
public static void main(String[] args) {
Map<User, String> map = new TreeMap<>();
map.put(new User(5, 90), "sun");
map.put(new User(2, 57), "li");
map.put(new User(3, 91), "hu");
for (Map.Entry<User, String> user : map.entrySet()) {
System.out.println(user.getKey().getId() + " " + user.getKey().getScore() + "=" + user.getValue());
}
}
}
class User implements Comparable<User> {
int id;
int score;
// 构造函数、get()、set()
@Override
public int compareTo(User o) {
// 按照user的id比较
return this.getId() - o.getId();
}
}
执行结果:
2 57=li
3 91=hu
5 90=sun
Process finished with exit code 0
4. key是自定义数据类型,且TreeMap构造函数传入了一个比较器
import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;
public class Demo {
public static void main(String[] args) {
Map<User, String> map = new TreeMap<>(new Comparator<User>() {
@Override
public int compare(User o1, User o2) {
// 构造函数传入的比较器,按照user的score比较
return o1.getScore() - o2.getScore();
}
});
map.put(new User(5, 90), "sun");
map.put(new User(2, 57), "li");
map.put(new User(3, 91), "hu");
for (Map.Entry<User, String> user : map.entrySet()) {
System.out.println(user.getKey().getId() + " " + user.getKey().getScore() + "=" + user.getValue());
}
}
}
class User {
int id;
int score;
// 构造函数、get()、set()
}
执行结果:
2 57=li
5 90=sun
3 91=hu
Process finished with exit code 0
5. key是自定义数据类型,实现了Comparable接口,且TreeMap构造函数传入了一个比较器,按照treemap构造函数传入的比较器比较
import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;
public class Demo {
public static void main(String[] args) {
Map<User, String> map = new TreeMap<>(new Comparator<User>() {
@Override
public int compare(User o1, User o2) {
// 构造函数传入的比较器,按照user的score比较
return o1.getScore() - o2.getScore();
}
});
map.put(new User(5, 90), "sun");
map.put(new User(2, 57), "li");
map.put(new User(3, 91), "hu");
for (Map.Entry<User, String> user : map.entrySet()) {
System.out.println(user.getKey().getId() + " " + user.getKey().getScore() + "=" + user.getValue());
}
}
}
class User implements Comparable<User> {
int id;
int score;
// 构造函数、get()、set()
@Override
public int compareTo(User o) {
// 按照user的id比较
return this.getId() - o.getId();
}
}
执行结果:可以看到是按照treemap构造函数传入的比较器进行比较的
2 57=li
5 90=sun
3 91=hu
Process finished with exit code 0
二、数据结构
TreeMap初始化的时候会初始化下列参数,Comparator是可以自己定义实现的一个比较的实现,默认为Null,那么默认的比较方式就是键的compare方法。root默认为Null。其中Entry实现了Map.entry<K,V> 接口,内部维护了left,right,parent,color 其中color默认是black。结合以下代码看下:
1. Entry
包括key-value、left、right、parent、color。
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;
//以及构造函数、get()、set()、equals()、hashcode()、toString()
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;
}
//toString(), 输出形式
public String toString() {
return key + "=" + value;
}
}
2. treemap
构造函数和一些属性,属性主要有comparator、root、size、modCount,一个treemap就是用root来表示的
public class TreeMap<K,V>
extends AbstractMap<K,V>
implements NavigableMap<K,V>, Cloneable, java.io.Serializable
{
/**维持tree map顺序的比较器,如果 comparator为空,按照key的自然顺序排序*/
private final Comparator<? super K> comparator;
/**treemap的根节点*/
private transient Entry<K,V> root;
/**treemap中的节点数量/
private transient int size = 0;
/**treemap结构的改变次数*/
private transient int modCount = 0;
/**构造一个新的、空treeMap,使用键值的自然顺序排序,TreeMap中的元素默认按照keys的自然排序排列。
(对Integer来说,其自然排序就是数字的升序;对String来说,其自然排序就是按照字母表排序)*/
public TreeMap() {
comparator = null;
}
/**按照compator的顺序排序*/
public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;
}
public TreeMap(Map<? extends K, ? extends V> m) {
comparator = null;
putAll(m);
}
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) {
}
}
三、基本操作
1. put(K key, V value)
public V put(K key, V value) {
Entry<K,V> t = root;
if (t == null) {//若根节点root为空,就根据key-value构造一个Entry对象作为根节点
compare(key, key); // type (and possibly null) check
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
int cmp;
Entry<K,V> parent;
// 执行构造函数时,传入Comparator就按Comparator比较,否则就按照键的Comparable比较顺序,
//从这里也可以看出,优先按照构造函数传入的比较器排序
Comparator<? super K> cpr = comparator;
//if和else两个分支,实现结果一样,都是找到当前key-value需要插入的位置,即parent节点
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);
}
else {
if (key == null)
throw new NullPointerException();
@SuppressWarnings("unchecked")
//当treemap构造函数未传入比较器,且自定义类未实现Comparable接口,报错 User cannot be cast to java.lang.Comparable,就是执行到这里出错
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);
}
//找到父节点,插入当前数据
Entry<K,V> e = new Entry<>(key, value, parent);
if (cmp < 0)
parent.left = e;
else
parent.right = e;
//插入当前节点,红黑树结构发生改变,可能不满足红黑树的定义,需要调整树结构,这个结构调整和红黑树的调整一样
fixAfterInsertion(e);
size++;
modCount++;
return null;
}
2. get(key)
public V get(Object key) {
Entry<K,V> p = getEntry(key);//根据key去找对应的value
return (p==null ? null : p.value);
}
final Entry<K,V> getEntry(Object key) {
//这里查找和二叉树的查找过程一样,只是分为两种方式,compartor和Comparable
//执行treemap构造函数时,会传入comparator,如果未传入comparator,就按照键实现的Comparable接口比较
//comparator不为空,就按照comparator去找,
if (comparator != null)
return getEntryUsingComparator(key);
if (key == null)
throw new NullPointerException();
// treemap执行构造函数时,comparator为null,按照键实现的Comparable接口的compareTo() 比较,
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;
// 查找过程和二叉树的查找过程一样
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;
}
final Entry<K,V> getEntryUsingComparator(Object key) {
@SuppressWarnings("unchecked")
K k = (K) key;
Comparator<? super K> cpr = comparator;
// 查找过程和二叉树的查找过程一样
if (cpr != null) {
Entry<K,V> p = root;
while (p != null) {
int cmp = cpr.compare(k, p.key);
if (cmp < 0)
p = p.left;
else if (cmp > 0)
p = p.right;
else
return p;
}
}
return null;
}
关于比较器,可以看这篇博客 https://blog.csdn.net/weixin_42041027/article/details/97390231