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

时间:2019-01-09 22:26:31

标签: java sorting java-8 java-stream collectors

I see there are many flavors of this "count occurrences and sort" type of questions (the closest related question was this) but none of them are working in my situation.

This is my code.

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()));

This results in

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

But I need it as

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

I tried this (reference):

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());

But getting this error

enter image description here

Now I am clueless. Can someone please help?

2 个答案:

答案 0 :(得分:4)

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

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(),但是两者都会产生相同的结果。

答案 1 :(得分:1)

Since you want the result as Map<String, Long>, you should not map to the entry key i.e. .map(Map.Entry::getKey) as we well as that you should not collect to a list either, i.e. .collect(toList()) as eventually you'll end up with List<String> instead of Map<String, Long>, rather after sorting by the specified criteria, you should collect to a map and in particular a 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));

The comparator above will sort by key only as that's what your expected result seems to suggest.


Note that if you don't need the limit operation after grouping the above can be simplified to:

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