当我们需要统计一个字符串中每个字符的出现次数时,一般的做法是我们会使用一个HashMap<String, Integer>来统计每一个字符的出现次数,进一步的要求,我们需要基于统计次数进行排序,这时就需要针对value来进行排序,如何实现,下面描述两种思路。

       无论哪种思路都需要实现一个比较器,比较器我们知道有comparable和comparator两种,一个是extends使用,一个是implements使用,具体两者的比较这里不说了,本文需要使用comparator,至于为什么这里也不解释了。自定义comparator的代码如下:


static class ByValueComparator implements Comparator<String> {
        HashMap<String, Integer> base_map;
 
        public ByValueComparator(HashMap<String, Integer> base_map) {
            this.base_map = base_map;
        }
 
        public int compare(String arg0, String arg1) {
            if (!base_map.containsKey(arg0) || !base_map.containsKey(arg1)) {
                return 0;
            }
 
            if (base_map.get(arg0) < base_map.get(arg1)) {
                return 1;
            } else if (base_map.get(arg0) == base_map.get(arg1)) {
                return 0;
            } else {
                return -1;
            }
        }
    }

思路1:创建一个TreeMap结构,使用上述比较器作为构造函数参数。具体代码如下


ByValueComparator cmp = new ByValueComparator(m);
	TreeMap<String, Integer> sorted_map = new TreeMap<String, Integer>(cmp);
        sorted_map.putAll(m);
        for (String name : sorted_map.keySet()) {
            System.out.printf("%s -> %d\n", name, m.get(name));
        }

        默认情况下TreeMap是按照key进行比较的,当然前提是Key的类型必须是可比较的不然编译器报错(基本上原始类型的封装类型都是可以比较的,若是自定义类型,择需要实现比较器了)。这里我们构造的时候没有使用默认的情况,而是传人自定义的比较器。那么在执行putAll的时候,就会按照自定义的比较器实现元素的插入操作,实现我们希望的基于Value排序。

但是上述方案实际上有一个问题,就是value的值可能是相同的,这个时候转换为TreeMap相同value的元素就只能保留一个,比如有两个Value都是4,那么TreeMap中则只会有一个Value=4的元素了,具体原因实际是和TreeMap的实现有关系的,因为TreeMap是基于比较来决定元素的位置的,而我们构造的比较器是基于值的比较,当值相同的时候,TreeMap就认为这个是相同的元素,从而导致了上述的结果。


思路2:基于value集合创建一个List,然后将List排序,具体代码如下


// 第二种方法
        List<String> keys = new ArrayList<String>(m.keySet());
        Collections.sort(keys, cmp);
        for(String key : keys) {
            System.out.printf("%s -> %d\n", key, m.get(key));
        }

        这个思路实现按value排序不会有问题,先按照value集合,对value进行排序,然后基于重排序的集合获取value的值和相应的key。