文章目录
问题描述
今天有一个需求,需要根据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。