今天有一个需求,需要根据map中的value进行排序,首先肯定就想到了TreeMap,于是实现了一个Comparator,代码如下:
class ValueComparator implements Comparator<Character> { Map<Character, Integer> base; public ValueComparator(Map<Character, Integer> base) { this.base = base; } public int compare(Character a, Character b) { if (base.get(a) >= base.get(b)) { return -1; } else { return 1; } } }
奇怪的是,排序没有问题,可以当我使用treeMap.get()时,永远返回null。
话不多说,直接debug,发现如下代码:
final Entry<K,V> getEntry(Object key) { // Offload comparator-based version for sake of performance if (comparator != null) return getEntryUsingComparator(key); if (key == null) throw new NullPointerException(); @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; }
原来treeMap和hashMap的get原理完全不同,hashMap会根据key的hash值找到对应的Node获取其中的value,而treeMap在有自定义Comparator的情况下,会使用自定义Comparator将所有的key和get中的key作比较,返回值为0的时候才会返回value。
由于我自定义的Comparator只有返回1和-1,所以永远得到的都是null。
class ValueComparator implements Comparator<Character> { Map<Character, Integer> base; public ValueComparator(Map<Character, Integer> base) { this.base = base; } public int compare(Character a, Character b) { if (base.get(a) == base.get(b)) { return 0; } else if (base.get(a) < base.get(b)){ return 1; } return -1; } }
由此可见,treeMap虽时候排序,但是get的效率相较于hashMap还是较低的,毕竟hashMap可以通过key的hash值直接定位到Node的位置,不考虑hash碰撞情况下,时间复杂度是O(1)。
而treeMap则是通过compare方法,比较大小来判断是否是当前的Entry,如果返回<0,往左边找,如果返回>0,往右边找,典型的二分查找,所以时间复杂度是O(log n),所以在没有排序需求的情况下,hashMap是大家做常用的Map。