Count occurrences in string and output sorted result by ocuurence_count desc, name

Viewed 89

Given a string:

String str = "Charlie Jennifer Charlie Bob Charlie Charlie Bob Jennifer Alice Alice";

Need to count name occurrences in the given string and output sorted result by occurrence count (desc), then by name. Here is my solution:

Arrays.stream(str.split(" "))
        .collect(groupingBy(identity(), counting())).entrySet().stream()
        .sorted(Map.Entry.<String, Long>comparingByValue()
                .reversed()
                .thenComparing(Map.Entry.comparingByKey()))
        .forEach(System.out::println);

My question is: do you know more elegant solution using Java Stream API ?

2 Answers

As mentioned in the comments, there's not really much to improve here.

Possibly, you could collect the results into a TreeMap and use lambda to compare by values without calling reversed():

Arrays.stream(str.split(" "))
      .collect(groupingBy(w -> w, TreeMap::new, counting()))
      .entrySet().stream()
      .sorted((a, b) -> b.getValue().compareTo(a.getValue()))
      .forEach(System.out::println);

Similarly, toMap collector could be used instead of groupingBy and counting:

 Arrays.stream(str.split(" "))
       .collect(toMap(w -> w, x -> 1, Long::sum, TreeMap::new))
       .entrySet().stream()
       .sorted((a, b) -> b.getValue().compareTo(a.getValue()))
       .forEach(System.out::println);

Maybe not using streams is a little bit less verbose but, still, you need to group first and then sort:

Map<String, Long> map = new HashMap<>();
for (String s : str.split(" ")) map.merge(s, 1L, Long::sum);

Here I'm using the Map.merge method to group words by frequency. Now we need to create a list from the entries and sort it:

List<Map.Entry<String, Long>> list = new ArrayList<>(map.entrySet());
list.sort(Map.Entry.comparingByValue()
             .reversed()
             .thenComparing(Map.Entry.comparingByKey()));

And now we have a list of Map.Entry in list, sorted as per your requirements.

If you need to create a map from these entries, it should be a LinkedHashMap, to preserve insertion order:

Map<String, Long> sortedByValue = new LinkedHashMap<>();
list.forEach(e -> sortedByValue.put(e.getKey(), e.getValue()));
Related