有序集合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;操作。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM