Skip to content
Advertisement

Reference antecedent Java stream step without breaking the stream pipeline?

I am new to functional programming, and I am trying to get better.

Currently, I am experimenting with some code that takes on the following basic form:

private static int myMethod(List<Integer> input){
      Map<Integer,Long> freq = input
        .stream()
        .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
      
      return (int) freq
        .keySet()
        .stream()
        .filter(key-> freq.containsKey(freq.get(key)))
        .count();
    }

First a hashmap is used to get the frequency of each number in the list. Next, we sum up the amount of keys which have their values that also exist as keys in the map.

What I don’t like is how the two streams need to exist apart from one another, where a HashMap is made from a stream only to be instantly and exclusively consumed by another stream.

Is there a way to combine this into one stream? I was thinking something like this:

private static int myMethod(List<Integer> input){
      return (int) input
        .stream()
        .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
        .keySet()
        .stream()
        .filter(key-> freq.containsKey(freq.get(key)))
        .count();
    }

but the problem here is there is no freq map to reference, as it is used as part of the pipeline, so the filter cannot do what it needs to do.

In summary, I don’t like that this collects to a hashmap only then to convert back into a keyset. Is there a way to “streamline” (pun intended) this operation to

  1. Not be going back and forth from stream and hashmap
  2. Reference itself in a way without needing to declare a separate map before the pipeline.

Thank you!

Advertisement

Answer

Your keySet is nothing but effectively a HashSet formed of your input. So, you should make use of temporary storage such that:

Set<Integer> freq = new HashSet<>(input);

and further count, filter based on values in a single stream pipeline as

return (int) input
        .stream()
        .collect(Collectors.groupingBy(Function.identity(),
                Collectors.counting()))
        .values() // just using the frequencies evaluated
        .stream()
        .filter(count -> freq.contains(count.intValue()))
        .count();
User contributions licensed under: CC BY-SA
6 People found this is helpful
Advertisement