计算列表中字符串的出现次数,然后对结果进行排序 - Java 8

Count occurrences of a String in a List, then sort the result - Java 8

我发现这种 "count occurrences and sort" 类型的问题有很多种(最相关的问题是 ),但其中 none 适合我的情况。

这是我的代码。

List<Employee> employees = new ArrayList<>();

Employee e1 = new Employee;
e1.setFirstName("Beth");

Employee e2 = new Employee;
e1.setFirstName("Beth");

Employee e3 = new Employee;
e1.setFirstName("Andrew");

// similarly I'm creating millions of employees and adding them to my list below 

employees.add(e1);
employees.add(e2);
employees.add(e3);
employees.add(e53456667);

//count occurrences of first names
Map<String, Long> employeeFirstNameCount = employees.stream()
                .collect(Collectors.groupingBy(p -> p.getFirstName(), Collectors.counting()));

这导致

{Beth=2, Andrew=34674, Charles=2388491, John=223545, Alex=2345562}

但我需要它作为

{Alex=2345562, Andrew=34674, Beth=2, Charles=2388491, John=223545}

我试过了):

Map<String, Long> employeeFirstNameCount = employees.stream()
        .collect(Collectors.groupingBy(p -> p.getFirstName(), Collectors.counting()))
        .entrySet().stream()
        .sorted(Map.Entry.<String, Long> comparingByValue(Comparator.naturalOrder()).thenComparing(Map.Entry.comparingByKey()))
        .limit(20)
        .map(Map.Entry::getKey)
        .collect(toList());

但是出现这个错误

现在我一头雾水。有人可以帮忙吗?

由于您希望结果为 Map<String, Long>,因此您不应映射到输入键,即 .map(Map.Entry::getKey),而且您也不应收集到列表,即 .collect(toList()) 因为最终你会得到 List<String> 而不是 Map<String, Long>,而不是在按指定的标准排序之后,你应该收集到地图,特别是 LinkedHashMap:

Map<String, Long> result = employees.stream()
                .collect(Collectors.groupingBy(p -> p.getFirstName(), 
                           Collectors.counting()))
                .entrySet().stream()
                .sorted(Map.Entry.<String, Long> comparingByKey())
                .limit(20)
                .collect(Collectors.toMap(Map.Entry::getKey, 
                    Map.Entry::getValue, 
                    (l,r) -> l, 
                    LinkedHashMap::new));

上面的比较器将仅按键排序,因为这似乎是您的预期结果所暗示的。


注意,如果分组后不需要limit操作,上面可以简化为:

employees.stream()
         .sorted(Comparator.comparing(Employee::getFirstName))
         .collect(Collectors.groupingBy(Employee::getFirstName,
                        LinkedHashMap::new,
                        Collectors.counting()));

如果在创建 Map 时使用 TreeMap 而不是默认的 HashMap,则可以获得所需的输出:

Map<String, Long> employeeFirstNameCount = employees.stream()
            .collect(Collectors.groupingBy(Employee::getFirstName,
                                           TreeMap::new,
                                           Collectors.counting()));

java.util.TreeMap 使用其键的自然顺序(这足以满足您的需要)或者您可以提供自定义 Comparator

注意我使用 lambda 表达式 Employee::getFirstName 而不是 p -> p.getFirstName(),但两者产生相同的结果。