• TreeMap集合类中的Comparator与Comparable接口详解


    1.comparable接口

    当我们运行关于treeMap示例代码1:

    代码示例
    package com.test.ComparatorTest;
    
    import java.util.*;
    
    public class Test {
        public static void main(String[] args) {
            Map students = new TreeMap();
            students.put(02,"邱烁铭");
            students.put(01,"李家雄");
            students.put(04,"李文高");
            students.put(03,"周云龙");
    
            Set st = students.keySet();     //获取键的集合
            Iterator it = st.iterator();       //获取Iterator对象
            while(it.hasNext()){
                Object key = it.next();
                Object value = students.get(key);       //根据获取的键值找到对应的值
                System.out.println(key + ":" + value);
            }
        }
    }
    

    结果如下:

    通过分析结果发现:
    ◆ 对插入的键值对按照键的升序进行了排序。
    ◆ TreeMap类是一个无序集合(输出顺序与插入顺序不同)

    造成此种现象的玄机就在TreeMap类的put方法中,put方法的部分源码如下所示:

    public V put(K key, V value){
        ……
        Entry<K,V> t = root;
        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);
        ……
    }
    

    TreeMap底层数据结构依赖于红黑树的实现,其核心在于比较待插入键值对中键的大小。因此,在put方法中我们将聚焦于compareTo方法。由于compareTo()是由变量k进行调用(k.compareTo()),不难发现,compareTo()是key强转后Comparable类型中的方法。查看comparable接口源码如下:

    public interface Comparable<T> {
        public int compareTo(T o);
    }
    

    comparable接口中只有一个compareTo(T o)的抽象方法。结合示例代码,我们put的键值对为Integer-String类型,通过观察Integer类发现其为comparable接口的实现类。Integer类部分源码如下所示:

    public final class Integer extends Number implements Comparable<Integer> {
                        ……
        public int compareTo(Integer anotherInteger) {
            return compare(this.value, anotherInteger.value);
        }
    
        public static int compare(int x, int y) {
            return (x < y) ? -1 : ((x == y) ? 0 : 1);
        }       
                        ……
    }
    

    总结:
    实例1中TreeMap类元素之所以能够排序是因为每次向TreeMap集合中存入键值对时,就会将键值取出通过调用compareTo()方法与其他元素的键值进行比较,该方法是Comparable接口中定义的。因此想要对集合中的元素进行排序,就必须实现Comparable接口。JDK中大部分类都实现Comparable接口,拥有接口中的CompareTo()方法,如Integer,String和double等。

    2.comparator接口

    有时候,定义的类没有实现Comparable接口或者实现了Comparable接口而不想按照定义的compareTo()方法进行排序。例如,希望按照降序进行排序。这时可以通过自定义比较器方式对TreeMap集合中的元素排序,即实现Comparator接口,在创建TreeMap集合时指定比较器。如实例2所示:

    public class Test {
        public static void main(String[] args) {
            Map students = new TreeMap(new Decreasing());
            students.put(02,"邱烁铭");
            students.put(01,"李家雄");
            students.put(04,"李文高");
            students.put(03,"周云龙");
    
            Set st = students.keySet();
            Iterator it = st.iterator();
            while(it.hasNext()){
                Object key = it.next();
                Object value = students.get(key);
                System.out.println(key + ":" + value);
            }
        }
    }
    

    Decreasing比较器源码如下:

    import java.util.Comparator;
    
    public class Decreasing implements Comparator {
    
        @Override
        public int compare(Object o1, Object o2) {
            int id1 = (Integer) o1;
            int id2 = (Integer) o2;
            return (id2 < id1) ? -1 : ((id1 == id2) ? 0 : 1);
        }
    }
    

    运行结果如下:

    通过结果对比发现,集合按照key值的降序进行排列。实例2中定义了比较器Decreasing针对Integer类型的key进行比较。其实现过程分析如下:
    由于我们在创建TreeMap对象使传入了比较器实例,因此在put方法中,将会运行以下代码:

        public V put(K key, V value) {
                …………
            Entry<K,V> t = root;
            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);
            }
                …………
        }
    

    根据源码分析,在比较键值时,会调用自定义比较器Decreasing中的compare方法对待插入节点的键值与其他节点的键值进行比对。然后按照自定义比较器制定的规则将节点插入到合适的位置。

    总结:
    实例2中TreeMap类元素之所以能够排序是因为每次向TreeMap集合中存入键值对时,就会将键值取出通过调用compare()方法与其他元素的键值进行比较,该方法是实现了Comparator接口的自定义比较器定义的。自定义比较器实现compare方法,并在创建TreeMap对象时传入。在用户层面,就能按照定义的compare规则对TreeMap集合进行排序。

  • 相关阅读:
    HTML大文件上传(博客迁移)
    微信小程序初探
    基于NodeJS微信公众号
    基于NodeJS的秘室聊天室
    CSS3 值得称赞新特性
    CAS FOR WINDOW ACTIVE DIRECTORY SSO单点登录
    IOS学习之-私人通讯录
    android 模拟2048
    使用ctypes在Python中调用C++动态库
    [Leetcode] Longest Palindromic Subsequence
  • 原文地址:https://www.cnblogs.com/miaowulj/p/14652443.html
Copyright © 2020-2023  润新知