zoukankan      html  css  js  c++  java
  • 有序集合TreeMap

    本文讨论的问题:TreeMap的key排序问题

    请看下面一个例子:

     TreeMap<String, String> map = new TreeMap<String, String>();
     map.put("f", "12345");
     map.put("b", "12345");
     map.put("e", "12345");
     map.put("d", "12345");
     map.put("g", "644");
     map.put("c", "980000000");
     map.put("a", "12345");
     System.out.println(map);
    

    输出结果是:

    {a=12345, b=12345, c=980000000, d=12345, e=12345, f=12345, g=644}
    

    为什么TreeMap对String类型的key值有排序效果呢?查看String类源码可知String类实现了Comparable接口,该接口中只有一个public int compareTo(T o);方法,对象的大小关系由返回值来确定,返回负整数,零,正整数表示当前对象小于,等于,大于指定对象。那么String类是如何实现的呢?

    public int compareTo(String anotherString) {
     int len1 = value.length;
     int len2 = anotherString.value.length;
     int lim = Math.min(len1, len2);
     char v1[] = value;
     char v2[] = anotherString.value;
    
     int k = 0;
     while (k < lim) {
     char c1 = v1[k];
     char c2 = v2[k];
     if (c1 != c2) {
     return c1 - c2;
     }
     k++;
     }
     return len1 - len2;
    }
    

    首先先比较相同长度内的每个字符是否相等,如果不等则返回该位置上两个字符的差值。如果都相等,那么就返回两者间length的差值。由此可见,存在一个效率问题,假设字符串str1由1万个a字符组成,字符串str2由一万个a字符串+b字符组成。那么在比较这两者大小的时候,就做了一万次无用的循环…,其实没想明白,为什么不先比较两者的length呢?

    如果不想使用String类本身的排序规则怎么办呢?TreeMap还提供了一个有参构造方法来自定义排序规则。写一个类StringComparator继承Comparator接口,实现compare方法

    package cn.horace.test;
    
    import java.util.Comparator;
    
    public class StringComparator implements Comparator<String> {
    
     @Override
     public int compare(String o1, String o2) {
     int len1 = o1.length();
     int len2 = o2.length();
     
     // 如果长度不等,那么直接返回差值
     if(len1 != len2)
     return len1 - len2;
     
     int lim = Math.min(len1, len2);
     char v1[] = o1.toCharArray();
     char v2[] = o2.toCharArray();
     int k = 0;
     while (k < lim) {
     char c1 = v1[k];
     char c2 = v2[k];
     if (c1 != c2) {
     return c1 - c2;
     }
     k++;
     }
     
     // 如果while循环中的return没执行,那么这两个字符串是相等的
     return 0;
     }
    
    }
    

    将StringComparator传入TreeMap的构造方法中

    TreeMap<String, String> map = new TreeMap<>(new StringComparator());
    map.put("f", "12345");
    map.put("b", "12345");
    map.put("e", "12345");
    map.put("d", "12345");
    map.put("g", "644");
    map.put("c", "980000000");
    map.put("a", "12345");
    System.out.println(map);
    

    运行结果如下所示

    {a=12345, b=12345, c=980000000, d=12345, e=12345, f=12345, g=644}
    

    可以看到效果是一样的,但从原理上比前者效率要高。为什么String类自身的比较器没起作用了呢?查看TreeMap源码可知,TreeMap在put的时候,首选传入的比较器,如果用户没有指定比较器则使用当前对象自身的比较器。如果自定义对象要作为TreeMap的key值时,则必须实现Comparator接口或者传入自定义比较器,否则将会抛出java.lang.ClassCastException异常,原因是当没i有自定义比较器的时候,TreeMap在put时会执行Comparable<? super K> k = (Comparable<? super K>) key;操作。

  • 相关阅读:
    模式对象管理
    Oracle数据库实例
    github使用简介
    Oracle数据库安装与连接与简介
    利益相关者分析
    问题账户需求分析
    2018春季学期需求工程概论阅读计划
    JAXB在Java 9/10并且使用Tomcat 9的问题
    mysql 备份 恢复
    IntelliJ IDEA安装bower
  • 原文地址:https://www.cnblogs.com/horace/p/4365375.html
Copyright © 2011-2022 走看看