4

I am running into a bit of an issue where I need order preserved in an operation being performed on a list of strings and using the collect method from the java streams api.

public List<String> countOccurrences(ArrayList<String> messages) {
        List<String> stackedMessages = new LinkedList<>();
        HashMap<String, Integer> messageOccurrences = 
                   messages.stream()
                           .collect(groupingBy(Function.identity(), summingInt(e -> 1)));

        messageOccurrences.forEach((key, value) -> {
            String appendString = value == 1 ? "" : " (" + value + " times)";
            stackedMessages.add(key + appendString);
        });

        return stackedMessages;
    }

The problem with the above code is if I process a list such as ["blah", "blah", "yep"], it returns ["yep", "blah (2 times)"] where I need it to return ["blah (2 times)", "yep"].

I looked at this post here already and was lead to believe if I am using a stream on an already ordered data structure then order would be ensured: How to ensure order of processing in java8 streams?

I'm thinking I need to change groupingBy to toMap and as of right now I am reading through that documentation. Anybody who is well versed in the subject matter already please offer some pointers.

UPDATE:

Thanks to the user @Aominè, this is the correct way to do it using groupingBy

.collect(groupingBy(Function.identity(),LinkedHashMap::new, summingInt(e->1)))
Stefan Zobel
  • 3,182
  • 7
  • 28
  • 38
Q.H.
  • 1,406
  • 2
  • 17
  • 33

1 Answers1

2

You’ll need to collect to a LinkedHashMap to get the expected result. Also, you don’t have to do another forEach separately from the stream pipeline after the groupingBy, just create a stream from the entrySet and map then collect to list.

return messages.stream()
               .collect(groupingBy(Function.identity(), LinkedHashMap::new, summingInt(e -> 1)))
               .entrySet()
               .stream()
               .map(e -> e.getKey()+(e.getValue() == 1 ? "" : " (" + e.getValue() +" times)"))
               .collect(toList());
Ousmane D.
  • 54,915
  • 8
  • 91
  • 126
  • I’d use `counting()` instead of `summingInt(e -> 1)`. And for the last mapping function, `e.getValue()==1? e.getKey(): e.getKey()+" ("+e.getValue()+" times)"` avoids unnecessary string concatenation. – Holger Jun 06 '18 at 07:41