一、概要
平时用的最多的就是HashMap,但是HashMap底层是数组加链表结构,对存储的key,会进行hash求存储位置,所以key是无序的
今天介绍一下有序的TreeMap和TreeSet的简单原理和操作
二、源码分析
TreeSet底层还是new的TreeMap,只是value用的是公共的Object对象,所以只介绍下TreeMap
- 数据结构
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; ... }
- 构造方法
默认构造方法是从小到达排序, 也能传入Comparator比较器
初始化对象//默认的构造方法,排序方式是从小到大 TreeMap<Integer, Integer> map1 = new TreeMap<>(); //提供的Comparator比较器支持自定义的排序方式,这里使用的是从大到小排序 TreeMap<Integer, Integer> map2 = new TreeMap<>((o1, o2) -> o2 -o1);
public TreeMap() { comparator = null; } public TreeMap(Comparator<? super K> comparator) { this.comparator = comparator; }
- put操作
当初始化Map对象时,只是初始化比较器,只有当put时才会开始初始化节点public V put(K key, V value) { Entry<K,V> t = root; if (t == null) { //判断首节点如果为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; ... 省略 ... }
三、TreeMap和TreeSet API的区别
TreeMap | TreeSet | 说明 | 备注 |
---|---|---|---|
V put(K key, V value) | boolean add(E e) | 新增或替换 | 有相同的key,则返回旧值(set只会返回true或者false) |
K ceilingKey(K key) | E ceiling(E e) | 从下往上最接近key的值,包含key(和排序有关 ) | 不存在则返回null |
K higherKey(K key) | E higher(E e) | 从下往上最接近key的值,不包含key(和排序有关 ) | 不存在返回null |
K floorKey(K key) | E floor(E e) | 从上往下最接近key的值,包含key(和排序有关 ) | 不存在返回null |
K lowerKey(K key) | K lower(K key) | 从上往下最接近key的值,不包含key(和排序有关 ) | 不存在返回null |
K firstKey() | E first() | 第一个节点 | 不存在返回null |
K lastKey() | E last() | 最后一个节点 | 不存在返回null |
boolean remove(Object o) | boolean remove(Object o) | 删除节点 | |
Map.Entry<K,V> ceilingEntry(K key) | 无 | 将满足条件的键值对取出来 |
下图展示了一些注意事项:
四、代码演示
public static void main(String[] args) {
//正序Map
TreeMap<Integer, Integer> ascMap = new TreeMap<>();
//倒序Map
TreeMap<Integer, Integer> descMap = new TreeMap<>((o1, o2) -> o2 -o1);
//插入相同的元素
ascMap.put(8, 8);
ascMap.put(5, 5);
ascMap.put(6, 6);
ascMap.put(10, 10);
ascMap.put(1, 1);
ascMap.put(3, 3);
descMap.put(8, 8);
descMap.put(5, 5);
descMap.put(6, 6);
descMap.put(10, 10);
descMap.put(1, 1);
descMap.put(3, 3);
//打印输出
Integer key1 = ascMap.ceilingKey(7); //key1: 8 从下往上最接近7的就是6
Integer key2 = descMap.ceilingKey(7); //key2: 6 从下往上最接近7的就是8
Integer key3 = ascMap.higherKey(7); //key3: 8 从下往上最接近但不等于7的就是8
Integer key4 = descMap.higherKey(7); //key4: 6 从下往上最接近但不等于7的就是6
Integer key5 = ascMap.ceilingKey(8); //key5: 8
Integer key6 = descMap.ceilingKey(8); //key6: 8
Integer key7 = ascMap.ceilingKey(10); //key7: 10
Integer key8 = ascMap.higherKey(10); //key8: null 从下往上最接近但不等于10, 不存在,返回null
}
五、小结
- HashMap底层用的是数组加链表,链表过长会转为红黑树,新增的key没有顺序;
- TreeMap底层用的是红黑树,有默认排序,也可以根据指定的排序器进行排序;
ceilingKey
和higherKey
都是基于红黑树从下往上
寻找最接近指定key的元素,区别是higherKey不能取等;floorKey
和lowerKey
都是基于红黑树从上往下
寻找最接近指定key的元素,区别是lowerKey不能取等。- HashMap对应的HashSet和TreeMap对应的TreeSet底层都是用的自己对应的Map,只是value会给默认的value值,一些api名字改了一下,其他的原理都相同。