TreeMap自定义compare后get永远返回null的问题

文章目录

问题描述

今天有一个需求,需要根据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。

上一篇:java中Comparator有什么用,举例说明?


下一篇:委托(Delegation)