Java集合中的TreeMap解读

 更新时间:2023年09月13日 09:12:34   作者:蕾峰  
这篇文章主要介绍了Java集合中的TreeMap解读,TreeMap可以传入一个实现了 Comparator接口的一个匿名内部类,匿名内部类里面我们仍然可以去指定添加我们的键值对的这种排序规则,需要的朋友可以参考下

TreeMap解读

TreeSet的底层是TreeMap。

public boolean add(E e) {
        return m.put(e, PRESENT)==null;
//其中的e为我们放进去的元素,作为key来存放的。
//而后面的value为PRESENT为一个固定的值,
    }

而其中的PRESENT是在treeSet里面创建的静态的final类型的Object.

private static final Object PRESENT = new Object();

如果我们使用TreeMap,里面放置的是(e为Key,PRESENT为对应的那一个值)。

我们通过具体的代码设计如下所示:

package com.rgf.map;
import java.util.TreeMap;
@SuppressWarnings({"all"})
public class TreeMap_ {
    public static void main(String[] args) {
        //使用默认的构造器,创建TreeMap,是无序的,是指输入输出的顺序不一致
        TreeMap treeMap = new TreeMap();
        treeMap.put("jack","杰克");
        treeMap.put("tom","汤姆");
        treeMap.put("kristina","克瑞斯提诺");
        treeMap.put("smith","斯密斯");
        System.out.println("treeMap="+treeMap);
    }
}
}

运行界面如下所示:

 我们查看如下所示:

我们发现TreeMap类里面除了提供无参的类以外,也有TreeMap(Comparator <K>)这样子的构造器。TreeMap可以传入一个实现了 Comparator接口的一个匿名内部类,匿名内部类里面我们仍然可以去指定添加我们的键值对的这种排序规则。

我们设计排序规则进行如下代码所示:

package com.rgf.map;
import java.util.Comparator;
import java.util.TreeMap;
@SuppressWarnings({"all"})
public class TreeMap_ {
    public static void main(String[] args) {
        //使用默认的构造器,创建TreeMap,是无序的,是指输入输出的顺序不一致
        //我们按照传入的k(String)的大小进行排序。
        TreeMap treeMap = new TreeMap(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                //我们按照传入的k(String)的大小进行排序。
                return ((String) o1).compareTo((String)o2);
            }
        });
        treeMap.put("jack","杰克");
        treeMap.put("tom","汤姆");
        treeMap.put("Kristina","克瑞斯提诺");
        treeMap.put("smith","斯密斯");
        System.out.println("treeMap="+treeMap);
    }
}

我们运行之后如下所示:

以上排序为从小到大,我们可以将排序修改为从大到小,我们进行修改代码如下所示:

package com.rgf.map;
import java.util.Comparator;
import java.util.TreeMap;
@SuppressWarnings({"all"})
public class TreeMap_ {
    public static void main(String[] args) {
        //使用默认的构造器,创建TreeMap,是无序的,是指输入输出的顺序不一致
        //我们按照传入的k(String)的大小进行排序。
        TreeMap treeMap = new TreeMap(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                //我们按照传入的k(String)的大小进行排序。(从小到大)
                return ((String) o2).compareTo((String)o1);
            }
        }) ;
        treeMap.put("jack","杰克");
        treeMap.put("tom","汤姆");
        treeMap.put("kristina","克瑞斯提诺");
        treeMap.put("smith","斯密斯");
        System.out.println("treeMap="+treeMap);
    }
}

运行界面如下所示:

我们可以按照长度进行排序(从小到大),我们将代码修改如下所示:

package com.rgf.map;
import java.util.Comparator;
import java.util.TreeMap;
@SuppressWarnings({"all"})
public class TreeMap_ {
    public static void main(String[] args) {
        //使用默认的构造器,创建TreeMap,是无序的,是指输入输出的顺序不一致
        //我们按照传入的k(String)的大小进行排序。
        TreeMap treeMap = new TreeMap(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                //我们按照传入的k(String)的长度大小进行排序。(从小到大)
                return ((String) o1).length()-((String) o2).length();
            }
        }) ;
        treeMap.put("jack","杰克");
        treeMap.put("tom","汤姆");
        treeMap.put("kristina","克瑞斯提诺");
        treeMap.put("smith","斯密斯");
        System.out.println("treeMap="+treeMap);
    }
}

我们的运行界面如下所示:

 我们也可以将排序找到长度从大到小,我们将代码修改如下所示:

package com.rgf.map;
import java.util.Comparator;
import java.util.TreeMap;
@SuppressWarnings({"all"})
public class TreeMap_ {
    public static void main(String[] args) {
        //使用默认的构造器,创建TreeMap,是无序的,是指输入输出的顺序不一致
        //我们按照传入的k(String)的大小进行排序。
        TreeMap treeMap = new TreeMap(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                //我们按照传入的k(String)的长度大小进行排序。(从小到大)
                return ((String) o2).length()-((String) o1).length();
            }
        }) ;
        treeMap.put("jack","杰克");
        treeMap.put("tom","汤姆");
        treeMap.put("kristina","克瑞斯提诺");
        treeMap.put("smith","斯密斯");
        System.out.println("treeMap="+treeMap);
    }
}

运行界面如下所示:

 我们来进行Debug,我们从如下代码开始进行debug:

TreeMap treeMap = new TreeMap(new Comparator()

源码

1.构造器

把传入的实现了Comparator接口的匿名内部类(对象),传给了TreeMap的comparator属性。

 //他的底层仍然是TreeMap,把传入的匿名内部类comparator赋给TreeMap的一个属性this.comparator.
 public TreeMap(Comparator<? super K> comparator) {
        this.comparator = comparator;
    }

我们退出去之后,进去put方法:

2.调用put方法

第一次添加,把k-v封装到Entry对象,放入root.

 源码如下所示;

public V put(K key, V value) {
        Entry<K,V> t = root;//此时root为空,将root赋值给t.
//我们第一次进去,所以还没有进行初始化,第一次初始化为空。
        if (t == null) {//此时t为空,从而进入里面代码。
            compare(key, key); // type (and possibly null) check
//比较key值是否相同,构造器判断规则不同,key值不同,当前规则为比较长度大小。则长度为key.
//TreeMap底层为Entry,Entry为TreeMap里面的一个内部类。
            root = new Entry<>(key, value, null);
            size = 1;
            modCount++;
            return null;
        }
        int cmp;
        Entry<K,V> parent;
        // split comparator and comparable paths
        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);
        }

我们进行第一次添加之后,我们如下所示:

以后添加,即要使用比较器进行比较后添加。

此时root里面已经有值了。此时添加第一个值得时候,没有添加比较器。我们继续进行添加,查看源码。

public V put(K key, V value) {
        Entry<K,V> t = root;
//由于此前已经有一个值了,此时root不为null.t也不为空,即不再进入下面第一个if语句。
        if (t == null) {
//第一次进去也调用了compare方法,即动态绑定了我们的匿名内部类的compare方法,传入的是两个key,即第一次添加的key加了两份进去。目的是为了判定是否为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;
        // split comparator and comparable paths
//由于已经有一个值了,所以我们会将比较器拿到,进入以下代码。
        Comparator<? super K> cpr = comparator;//比较器传入的值赋值给cpr
        if (cpr != null) {
            do { //遍历所有的key,给当前key找到适当的位置
                parent = t; //我们传入的comparator赋值给parent
                cmp = cpr.compare(key, t.key); //动态绑定到我们的匿名内部类的compare方法。
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else  //如果遍历过程中,发现准备添加key和当前已有的key相等,就不添加了,但是会进行覆盖原来的值。是由我们自定义的compare方法来决定的。
                    return t.setValue(value);
            } while (t != null);
        }

 我们会动态绑定到我们的匿名内部类:

我们调用了compare方法了:

 我们继续往进走:

  final int compare(Object k1, Object k2) {
//如果comparator不等于null的时候,我们进行动态绑定到我们的匿名内部类的compare方法。
        return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2)
            : comparator.compare((K)k1, (K)k2);
    }

 我们会动态绑定到我们的匿名内部类:

此时的o1和o2都是jack.但是这个结果对是否添加当前的value值有任何影响。主要是为了检测是否为null。如果为null,则会在comparator方法里面抛出异常。

这里的compare作用是查看key是否为空,还有就是判断当前key是否实现了compareable接口是否是可以进行比较的。

我们进行排序的示例如下所示:  

package com.rgf.jihe;
import org.junit.Test;
import java.util.*;
/**
 * 向TreeMap中添加key-value,要求key必须是由同一个类创建的对象
 * 因为要按照key进行排序:自然排序、定制排序
 *
 */
public class TreeMapTest {
    public static void main(String[] args) {
        //自然排序
        TreeMap map = new TreeMap();
        User u1 = new User("Tom",23);
        User u2 = new User("Jerry",32);
        User u3 = new User("Jack",20);
        User u4 = new User("Rose",18);
        map.put(u1,98);
        map.put(u2,89);
        map.put(u3,76);
        map.put(u4,100);
        Set set = map.entrySet();
        Iterator iterator = set.iterator();
        while (iterator.hasNext()){
            Object next = iterator.next();
            System.out.println(next);
        }
    }
    //定制排序
    @Test
    public  void test2(){
        TreeMap map = new TreeMap(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                if(o1 instanceof User && o2 instanceof User){
                    User user=(User) o1;
                    User user1=(User) o2;
                    return  Integer.compare(user.getAge(),user1.getAge());
                }
                throw new RuntimeException("输入的类型不匹配");
            }
        });
        User user = new User("rgf",123);
        User user1 = new User("woer",456);
        User user2 = new User("fed",789);
       map.put(user1,55);
       map.put(user,66);
       map.put(user2,77);
        Set set = map.entrySet();
        Iterator iterator = set.iterator();
        while (iterator.hasNext()){
            Object next = iterator.next();
            System.out.println(next);
        }
    }
}
class User implements  Comparable{
    private String name;
    private int age;
    public User() {
    }
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
    //我们写出来该方法则不会再继续报错。
//按照姓名从大到小排列,年龄从小到大排列
    @Override
    public int compareTo(Object o) {
        if(o instanceof User){
            User User=(com.rgf.jihe.User)o;
            //return -this.name.compareTo(user.name);为了可以确保相同的值可以进行排序,我们进行二级排序
            int compare=-this.name.compareTo(User.name);
            if(compare!=0){//如果相等,则我们进行比较年龄
                return compare;
            }else {
                return Integer.compare(this.age,User.age);
            }
        }else {
            throw  new RuntimeException("输入的类型不匹配");
        }
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User User = (com.rgf.jihe.User) o;
        if (age != User.age) return false;
        return Objects.equals(name, User.name);
    }
    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + age;
        return result;
    }
}
 

运行之后如下所示:

到此这篇关于Java集合中的TreeMap解读的文章就介绍到这了,更多相关TreeMap解读内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java语言求解完美数代码分析

    Java语言求解完美数代码分析

    这篇文章主要介绍了Java语言求解完美数代码分析,具有一定参考价值,需要的朋友可以了解下。
    2017-12-12
  • 基于@RequestParam与@RequestBody使用对比

    基于@RequestParam与@RequestBody使用对比

    这篇文章主要介绍了@RequestParam与@RequestBody的使用对比,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10
  • Java中HashMap集合的6种遍历方式详解

    Java中HashMap集合的6种遍历方式详解

    这篇文章主要介绍了Java中HashMap集合的6种遍历方式详解,HashMap 基于哈希表的 Map 接口实现,是以 key-value 存储形式存在,即主要用来存放键值对,HashMap 的实现不是同步的,这意味着它不是线程安全的,我们来看一下其遍历方式,需要的朋友可以参考下
    2023-12-12
  • SpringBoot日期格式转换之配置全局日期格式转换器的实例详解

    SpringBoot日期格式转换之配置全局日期格式转换器的实例详解

    这篇文章主要介绍了SpringBoot日期格式转换之配置全局日期格式转换器的实例详解,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-12-12
  • java 注解实现一个可配置线程池的方法示例

    java 注解实现一个可配置线程池的方法示例

    这篇文章主要介绍了java 注解实现一个可配置线程池的方法示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-01-01
  • SpringBoot MockMvc单元测试的示例代码

    SpringBoot MockMvc单元测试的示例代码

    这篇文章主要介绍了SpringBoot MockMvc单元测试的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-11-11
  • java实现发送邮件的示例代码

    java实现发送邮件的示例代码

    这篇文章主要介绍了java如何实现发送邮件,文中示例代码非常详细,帮助大家更好的理解和学习,感兴趣的朋友可以了解下
    2020-07-07
  • maven profile自动切换环境参数的2种方法详解

    maven profile自动切换环境参数的2种方法详解

    这篇文章主要给大家介绍了关于maven profile自动切换环境参数的2种方法,文中通过示例代码将这两种方法介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
    2018-04-04
  • Java如何正确处理下载文件时HTTP头的编码问题

    Java如何正确处理下载文件时HTTP头的编码问题

    这篇文章主要介绍了Java如何正确处理下载文件时HTTP头的编码问题,
    通常HTTP消息包括客户机向服务器的请求消息和服务器向客户机的响应消息,今天来讲解下正确处理下载文件时HTTP头的编码问题,需要的朋友可以参考下
    2023-07-07
  • SPRINGMVC JSON数据交互如何实现

    SPRINGMVC JSON数据交互如何实现

    这篇文章主要介绍了SPRINGMVC JSON数据交互如何实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-06-06

最新评论