1、TreeMap
构造
public TreeMap() {
comparator = null;
}
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) {
}
}
无参构造中comparator是null。
成员
//比较器,注意这里是final修饰的
private final Comparator<? super K> comparator;
//根节点
private transient Entry<K,V> root;
//树中数据的总数
private transient int size = 0;
//修改次数
private transient int modCount = 0;
TreeMap.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;
}
}
可以发现TreeMap的比HashMap结构简单很多,没有容量、扩容、装载因子等概念,也不再是数组+链表+红黑树,貌似单纯就是一颗红黑树。
2、put方法
public V put(K key, V value) {
Entry<K,V> t = root;
//根节点为空,直接插入
if (t == null) {
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<? 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);
}
//自定义比较器为空时,使用自然排序
else {
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);
}
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;
}
final int compare(Object k1, Object k2) {
return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2)
: comparator.compare((K)k1, (K)k2);
}
当自定义比较器不为空时,调用comparator的compare方法对key进行比较;当自定义比较器为空,调用key的compareTo方法。因此,TreeMap的key一定要实现Comparable接口。
TreeMap的key不能为null,因为put时会调用compareTo或者compare方法,对于为null的key会抛出NullPointerException。而HashMap的key是可以为null的。
@Test
public void testTreeMap(){
Map<String, Integer> hashMap = new HashMap<>();
hashMap.put(null, 1);
System.out.println("hashMap: " + hashMap);
Map<String, Integer> treeMap = new TreeMap<>();
treeMap.put(null, 1);
System.out.println("treeMap: " + treeMap);
}
3、HashMap与TreeMap比较
- HashMap底层使用数组,容量存在上限,Integer的最大值,而TreeMap没有;
- HashMap中的红黑树使用的是key的hash值进行排序,而TreeMap使用比较器的compare或者key的compareTo方法进行比较大小,从而排序;
- HashMap可加入key为null的键,而TreeMap不可以;
- HashMap的key需要实现hashCode和equals方法,而TreeMap不需要,但是需要实现接口Comparable或者自定义比较器;
- 查询、添加、删除的时间复杂度上,TreeMap为log(n),HashMap若不存在碰撞,则为O(1),存在碰撞则看存储结构是链表还是红黑树,链表则为O(n),红黑树则为log(n),总的来说HashMap直接定位到桶位置是O(1)的,效率高于TreeMap。
@Test
public void testTreeMap2(){
Map<TestBo, Integer> treeMap = new TreeMap<>();
treeMap.put(new TestBo("a"), 1);
treeMap.put(new TestBo("a"), 2);
System.out.println("treeMap: "+treeMap);
Map<TestBo, Integer> hashMap = new HashMap<>();
hashMap.put(new TestBo("a"), 1);
hashMap.put(new TestBo("a"), 2);
System.out.println("hashMap: "+hashMap);
}
class TestBo implements Comparable<TestBo>{
String t1;
public TestBo(String t1) {
this.t1 = t1;
}
@Override
public int compareTo(TestBo o) {
return this.t1.compareTo(o.t1);
}
@Override
public String toString() {
return "TestBo{" +
"t1='" + t1 + '\'' +
'}';
}
}
4、SortedMap中一些好用的方法
public void testTreeMap3(){
SortedMap<String, Integer> treeMap = new TreeMap<>();
treeMap.put("c", 1);
treeMap.put("b", 2);
treeMap.put("d", 3);
treeMap.put("a", 4);
treeMap.put("e", 5);
treeMap.put("f", 6);
treeMap.put("h", 7);
treeMap.put("i", 8);
System.out.println("treeMap: " + treeMap);
//排序后的第一个元素
System.out.println("firstKey: "+treeMap.firstKey());
//排序后的最后一个元素
System.out.println("lastKey: "+treeMap.lastKey());
//取小于某个key的所有值
System.out.println("headMap: "+treeMap.headMap("d"));
//取大于等于b,小于d的所有值
System.out.println("subMap: "+treeMap.subMap("b", "d"));
//取大于等于d的所有值
System.out.println("tailMap: "+treeMap.tailMap("d"));
}
5、使用TreeMap实现一致性hash
参考:TreeMap实现一致性hash
如下的例子可以这样理解:
Node为Redis节点,Obj为缓存对象,刚开始使用3个节点缓存,当扩容时使用就近寻址找到最近节点,不至于造成缓存大面积失效。
@Test
public void testTreeMap4(){
NodeArray nodeArray = new NodeArray();
Node[] nodes = {
new Node("Node--> 1"),
new Node("Node--> 2"),
new Node("Node--> 3")
};
for (Node node : nodes) {
nodeArray.addNode(node);
}
Obj[] objs = {
new Obj("1"),
new Obj("2"),
new Obj("3"),
new Obj("4"),
new Obj("5")
};
for (Obj obj : objs) {
nodeArray.put(obj);
}
validate(nodeArray, objs);
}
private void validate(NodeArray nodeArray, Obj[] objs) {
for (Obj obj : objs) {
System.out.println(nodeArray.get(obj));
}
//再添加两个Node验证
nodeArray.addNode(new Node("anything1"));
nodeArray.addNode(new Node("anything2"));
System.out.println("========== after =============");
for (Obj obj : objs) {
System.out.println(nodeArray.get(obj));
}
}
class Node{
Map<Integer, Obj> node = new HashMap<>();
String name;
Node(String name) {
this.name = name;
}
public void putObj(Obj obj) {
node.put(obj.hashCode(), obj);
}
Obj getObj(Obj obj) {
return node.get(obj.hashCode());
}
@Override
public int hashCode() {
return name.hashCode();
}
}
class Obj{
String key;
Obj(String key) {
this.key = key;
}
@Override
public int hashCode() {
return key.hashCode();
}
@Override
public String toString() {
return "Obj{" +
"key='" + key + '\'' +
'}';
}
}
class NodeArray{
//按照node的hash值进行排序
TreeMap<Integer, Node> nodes = new TreeMap<>();
void addNode(Node node) {
nodes.put(node.hashCode(), node);
}
void put(Obj obj) {
int objHashcode = obj.hashCode();
Node node = nodes.get(objHashcode);
if (node != null) {
node.putObj(obj);
return;
}
// 找到比给定 key 大的集合
SortedMap<Integer, Node> tailMap = nodes.tailMap(objHashcode);
// 找到最小的节点
int nodeHashcode = tailMap.isEmpty() ? nodes.firstKey() : tailMap.firstKey();
nodes.get(nodeHashcode).putObj(obj);
}
Obj get(Obj obj) {
Node node = nodes.get(obj.hashCode());
if (node != null) {
return node.getObj(obj);
}
// 找到比给定 key 大的集合
SortedMap<Integer, Node> tailMap = nodes.tailMap(obj.hashCode());
// 找到最小的节点
int nodeHashcode = tailMap.isEmpty() ? nodes.firstKey() : tailMap.firstKey();
return nodes.get(nodeHashcode).getObj(obj);
}
}