Search code examples
javalambdahashmapjava-stream

Count frequency (Specialized) in Java 8 using Lambdas


I have a list of strings like this: "na","na","na","uk","uk". I want to count the frequency of each element in such a way that if the existing map's value is even, then I will add 1; otherwise 2.

 List<String> streamer = Arrays.asList("na", "na", "na", "uk", "uk");
 Map<String, Integer> m = new HashMap<>();

 for(String s:streamer) {
     if (m.containsKey(s)) {
         if(m.get(s) % 2 == 0)
             m.put(s, m.get(s) + 1);
         else
             m.put(s,m.get(s)+2);
     }
     else
         m.put(s,1);
  }
  System.out.println("CUSTOM Frequency::::" + m); 

Now, I want to achieve the exact same thing using streams and lambdas.

All I could do is this:

Map<String, Long>map4 = streamer.stream()
    .collect(Collectors.groupingBy(
        Function.identity(),
        Collectors.counting())); // How can I get custom counting instead of Collectors.counting() ?

Solution

  • Besides @Sweeper's answer to get the expected counting result, if your question also implied how to customize a collect operation, you could use the method Collector.of().

    The method accepts a supplier, a BiConsumer as the accumulator, and a BinaryOperator as the combiner. The supplier simply provides the container where to store the result, in your case a Map<String, Long>. The accumulator populates the container by putting or updating an entry with the custom frequency logic. Lastly, The combiner simply merges the sub-results of parallel executions in case the stream is executed as a parallel stream.

    List<String> streamer = Arrays.asList("na", "na", "na", "uk", "uk");
    Map<String, Long> map4 = streamer.stream()
            .collect(Collector.of(
                    HashMap::new,
                    (Map<String, Long> map, String s) -> {
                        if (!map.containsKey(s)) {
                            map.put(s, 1L);
                        } else {
                            map.computeIfPresent(s, (String key, Long val) -> val % 2 != 0 ? val + 2 : val + 1);
                        }
                    },
                    (Map<String, Long> map1, Map<String, Long> map2) -> {
                        for (Map.Entry<String, Long> entry : map2.entrySet()) {
                            map1.computeIfPresent(entry.getKey(), (key, val) -> val + entry.getValue() + 1);
                            map1.computeIfAbsent(entry.getKey(), key -> entry.getValue());
                        }
                        return map1;
                    }
            ));
    

    Output

    {na=5, uk=3}