Java集合之TreeMap

一. 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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值