java8 Lambda表达式实现Map中按照Value排序

半兽人 发表于: 2018-12-31   最后更新时间: 2018-12-31 14:11:23  
{{totalSubscript}} 订阅, 5,960 游览

在以前的思路我们的做法如下:

/**
 * 
 * Map根据value排序;
 * 
 * @param map
 * @return
 */
public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue(Map<K, V> map) {
    List<Map.Entry<K, V>> list = new LinkedList<>(map.entrySet());
    Collections.sort(list, new Comparator<Map.Entry<K, V>>() {
        @Override
        public int compare(Map.Entry<K, V> o1, Map.Entry<K, V> o2) {
            return (o2.getValue()).compareTo(o1.getValue());
        }
    });

    Map<K, V> result = new LinkedHashMap<>();
    for (Map.Entry<K, V> entry : list) {
        result.put(entry.getKey(), entry.getValue());
    }
    return result;
}

先把Map变成可排序的List使用Comparator接口对entry进行排序,, 我们需要做一些简化.

第一步: 使用Lambda表达式先对Comparator接口做简化, 代码会变成如下情况:

public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue(Map<K, V> map) {
    List<Map.Entry<K, V>> list = new LinkedList<>(map.entrySet());

    list.sort(Comparator.comparing(Entry::getValue));

    Map<K, V> result = new LinkedHashMap<>();
    for (Map.Entry<K, V> entry : list) {
        result.put(entry.getKey(), entry.getValue());
    }
    return result;
}

这样的话, 一行代码就代替了五行, 但是会有个问题, 这样写只能从小到大排序很不灵活, 我们还有其他办法.来看下面的代码:

public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue(Map<K, V> map) {
    List<Map.Entry<K, V>> list = new LinkedList<>(map.entrySet());

    list.sort((o1, o2)-> o2.getValue().compareTo(o1.getValue()));

    Map<K, V> result = new LinkedHashMap<>();
    for (Map.Entry<K, V> entry : list) {
        result.put(entry.getKey(), entry.getValue());
    }
    return result;
}

用lambda表达式就可以做到变换排序的方式, 只要改变o1和o2的顺序就可以了.还是很长, 再少几句代码, 怎么办?

我们来分析下最原始的排序代码 ---> 首先是将Map转化为List<Entry>利用List的可排序的特性排序后遍历到新的Map里面去, 这样就很简单了, 我们可以从遍历的地方入手.代码如下:

public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue(Map<K, V> map) {
    List<Map.Entry<K, V>> list = new LinkedList<>(map.entrySet());

    list.sort((o1, o2)-> o2.getValue().compareTo(o1.getValue()));

    Map<K, V> result = new LinkedHashMap<>();
    list.stream().forEach(entry -> result.put(entry.getKey(), entry.getValue()));

    return result;
}

也许做到上面这一步已经很满足了, 可是作为一个优秀的开发人员怎么能满足于这种程度, 我们要用两句话完成上面的功能.我们可以发现entrySet()是个集合, stream是有sort方法的, 可以set变成stream然后sort之后forEach到新的Map中, 牛逼吧, 废话少说,看代码.

public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue(Map<K, V> map) {
    Map<K, V> sortMap = new LinkedHashMap<>();
    new map.entrySet().stream()
            .sorted((o1, o2) -> o2.getValue().compareTo(o1.getValue()))
            .forEach(entry -> sortMap.put(entry.getKey(), entry.getValue()));
    return sortMap;
}

下面提供一个工具类

/**
 * flag = 1 正序
 * flag = 0 倒序
 * @param map
 * @param flag
 * @return
 */
public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue(Map<K, V> map, int flag) {
    Map<K, V> sortMap = new LinkedHashMap<>();
    if(flag == 1) {
        map.entrySet().stream()
        .sorted((o1, o2) -> o1.getValue().compareTo(o2.getValue()))
        .forEach(entry -> sortMap.put(entry.getKey(), entry.getValue()));
    } else {
        map.entrySet().stream()
        .sorted((o1, o2) -> o2.getValue().compareTo(o1.getValue()))
        .forEach(entry -> sortMap.put(entry.getKey(), entry.getValue()));
    }
    return sortMap;
}

以上的代码已经够简洁了, 但是有一个中间变量, 我作为一个究极程序员是看不惯的, 能不能把它也省略掉一句代码实现整个功能呢? 答案是可以的.

public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue2(Map<K, V> map, int flag) {

    if(flag == 1) {
        return map.entrySet().stream().sorted((o1, o2) -> o1.getValue().compareTo(o2.getValue())).map(entry -> {
            Map<K, V> result = new LinkedHashMap<>();
            result.put(entry.getKey(), entry.getValue());
            return result;
        }).reduce((map1, map2) -> {
            map2.entrySet().forEach(entry -> map1.put(entry.getKey(), entry.getValue()));
            return map1;
        }).get();
    } else {
        return map.entrySet().stream().sorted((o1, o2) -> o2.getValue().compareTo(o1.getValue())).map(entry -> {
            Map<K, V> result = new LinkedHashMap<>();
            result.put(entry.getKey(), entry.getValue());
            return result;
        }).reduce((map1, map2) -> {
            map2.entrySet().forEach(entry -> map1.put(entry.getKey(), entry.getValue()));
            return map1;
        }).get();
    }

思路是做好排序后将排序后的entry加入到新的Map里面, 再将stream<Map<K,V>>进行叠加, 可能有些抽象, 不能明白的也只能帮到这啦.

更新于 2018-12-31

查看java更多相关的文章或提一个关于java的问题,也可以与我们一起分享文章