TreeMap底层原理学习(JDK1.8)

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);
        }
    }
发布了33 篇原创文章 · 获赞 9 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_36142042/article/details/105104007