java中经常排序,但是自己对Map的排序方法一直不是很清楚,特此记录。
Map作为键值对的存储工具,基本的概念介绍网上都有,自己参考如下博客:


简单介绍Map
在讲解Map排序之前,我们先来稍微了解下map。map是键值对的集合接口,它的实现类主要包括:HashMap,TreeMap,Hashtable以及LinkedHashMap等。其中这四者的区别如下(简单介绍)一般使用HashMap和TreeMap

  • HashMap:我们最常用的Map,它根据key的HashCode 值来存储数据,根据key可以直接获取它的Value,同时它具有很快的访问速度。HashMap最多只允许一条记录的key值为Null(多条会覆盖);允许多条记录的Value为 Null。非同步的。
  • TreeMap: 能够把它保存的记录根据key排序,默认是按升序排序,也可以指定排序的比较器,当用Iterator 遍历TreeMap时,得到的记录是排过序的。TreeMap不允许key的值为null。非同步的。
  • Hashtable: 与 HashMap类似,不同的是:key和value的值均不允许为null;它支持线程的同步,即任一时刻只有一个线程能写Hashtable,因此也导致了Hashtale在写入时会比较慢。
  • LinkedHashMap: 保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的.在遍历的时候会比HashMap慢。key和value均允许为空,非同步的。

本文正文下面式排序方式的介绍:

Map的排序方式一 直接使用TreeMap进行排序

public class test {
    public static void main(String[] args) {
        Map<String, String> map = new TreeMap<>();
        map.put("a", "I'am a");
        map.put("c", "I'am c");
        map.put("d", "I'am d");
        map.put("b", "I'am b");
        for (Map.Entry<String, String> entry : map.entrySet()) {
            System.out.println(entry.getKey() + " " + entry.getValue());
        }
    }
}

输出的结果如下:

a I’am a
b I’am b
c I’am c
d I’am d

但是上面的排序默认是按照Key值进行排序的,并且按照的是默认的升序排序,如果我们想改变默认的排序方式,即按照我们自己的比较方式排序,我们就需要实现自己的比较器,实现方式如下(下面的这种实现方式只能根据key值进行排序):
下面的代码实现了根据key值进行降序排序

public class test {
    public static void main(String[] args) {
        Map<Integer, String> map = new TreeMap<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        });
        // 上面的Map可也以写成lambda表达式的形式    Map<Integer, String> map = //new TreeMap<>((o1, o2) -> o2 - o1);

        map.put(3, "I'am 3");
        map.put(0, "I'am 0");
        map.put(2, "I'am 2");
        map.put(1, "I'am 1");
        for (Map.Entry<Integer, String> entry : map.entrySet()) {
            System.out.println(entry.getKey() + " " + entry.getValue());
        }
    }
}

输出:
3 I’am 3
2 I’am 2
1 I’am 1
0 I’am 0

以上的排序方法都是根据Key值进行排序,如果我们想根据value进行排序或者根据value和key进行排序,我们可以使用如下的方法

首先将map中的值转存到list中,然后对List进行排序,之后将list排序结果取出,再存储到LinkedHashMap中

public class test {
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();
        map.put("I am d", "aaad");
        map.put("I am c", "cs");
        map.put("I am b", "baa");
        map.put("I am a", "a");
        List<Map.Entry<String, String>> list = new LinkedList<>(map.entrySet());


        // 下面的也可以写成lambda表达式这种形式
        // Collections.sort(list, (o1, o2) -> o2.getValue().compareTo(o1.getValue()));
        Collections.sort(list, new Comparator<Map.Entry<String, String>>() {
            @Override
            public int compare(Map.Entry<String, String> o1, Map.Entry<String, String> o2) {
                return o2.getValue().compareTo(o1.getValue()); // 这里改为根据value值进降序排序,这里也可以改成根据key和value进行排序
            }
        });
        Map<String, String> result = new LinkedHashMap<>();
        for (Map.Entry<String, String> entry : list) {
            result.put(entry.getKey(), entry.getValue());
        }
        System.out.println(result);
    }
}

输出结果:
{I am c=cs, I am b=baa, I am d=aaad, I am a=a}
这种自己平常不太使用,自己通常使用stream进行排序。

Map排序方法二:使用stream进行排序

Map<Integer, String> map = new HashMap<>();
        map.put(100, "I'am a");
        map.put(99, "I'am c");
        map.put(2, "I'am d");
        map.put(33, "I'am b");
        // 按照value进行倒排,如果要根据key进行排序的话使用Map.Entry.comparingByKey()
        map.entrySet().stream().sorted(Collections.reverseOrder(Map.Entry.comparingByValue()))
                .forEach(System.out::println);
        // 根据value进行正序排序,根据key进行排序的话使用comparingByKey()
        map.entrySet().stream().sorted(Map.Entry.comparingByValue()).forEach(System.out::println);

        // 如果要将排序的结果返回的话,我们可以使用下面的方法(注意:要使用LinkedHashMap进行保存,linkedHashMap可以保存插入顺序)
        Map<Integer, String> resultMap1 = map.entrySet().stream().sorted(Map.Entry.comparingByValue()).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (s, s2) -> s,
                LinkedHashMap::new));

        // 下面的这种写法同上面的写法,只不过是将简写展开了,这样更易于理解
        Map<Integer, String> resultMap = map.entrySet().stream().sorted(Map.Entry.comparingByValue()).collect(Collectors.toMap(new Function<Map.Entry<Integer, String>, Integer>() {
            @Override
            public Integer apply(Map.Entry<Integer, String> integerStringEntry) {
                return integerStringEntry.getKey();
            }
        }, new Function<Map.Entry<Integer, String>, String>() {
            @Override
            public String apply(Map.Entry<Integer, String> integerStringEntry) {
                return integerStringEntry.getValue();
            }
        }, new BinaryOperator<String>() {
            @Override
            public String apply(String s, String s2) {
                return s;
            }
        }, new Supplier<Map<Integer, String>>() {
            @Override
            public Map<Integer, String> get() {
                return new LinkedHashMap<>();
            }
        }));

        // 同样如果需要将排序的将结果作为Map进行返回我们还可以使用下面的方法,但是不推荐这种方式(effectivejava 178页中说:foreach操作应该只用于报告stream计算的结果,而不是执行计算)
        Map<Integer, String> result2 = new LinkedHashMap<>();
        map.entrySet().stream().sorted(Map.Entry.comparingByValue()).forEach(new Consumer<Map.Entry<Integer, String>>() {
            @Override
            public void accept(Map.Entry<Integer, String> integerStringEntry) {
                result2.put(integerStringEntry.getKey(), integerStringEntry.getValue());
            }
        });