Reputation: 2516
I have a stream of elements and I want transform it to another stream that would consist only of elements that appeared the most in the previous stream assuming that I may have multiple elements with the same number of occurrences.
As an example:
Input stream: ['A', 'B', 'A', 'B', 'C', 'C', 'A', 'B']
Output stream: ['A', B'] as they both had max number of occurrences in the previous stream equal to 3.
So far I've written the following code:
stream.collect(toMap(w -> w, w -> 1, Integer::sum))
.entrySet()
.stream()
.max(comparing(Map.Entry::getValue))
.map(Map.Entry::getKey);
But this allows me to get only one of the elements with the max number of occurrences and for the case above it would give either 'A' or 'B'.
Is there any elegant way of how this can be achieved using lambdas?
Thanks, Cheers
Upvotes: 4
Views: 2868
Reputation: 21124
In the first place, why do you start with a Stream<Character>
. You can start with an array of chars and then use the iteration to build up a map from char to count and in the meantime calculate the max value in the map while iterating. Then use this map and the max value intermediate result to get the final result. This approach just passes over the collection only twice. Here's how it looks given that you have a char[]
named chars
upfront.
final Map<Character, Integer> charCntMap = new HashMap<>();
int maxCnt = 0;
for (char c : chars) {
charCntMap.merge(c, 1, (a, b) -> a + b);
final int charCnt = charCntMap.get(c);
if (maxCnt < charCnt)
maxCnt = charCnt;
}
final int maxVal = maxCnt;
List<Character> maxOccuringChars = charCntMap.entrySet().stream()
.filter(e -> e.getValue().intValue() == maxVal)
.map(Map.Entry::getKey)
.collect(Collectors.toList());
Output:
[A, B]
Upvotes: 1
Reputation: 16359
There's no easy way to do it in one go, but you can sort the map to find the maximum value, or just use IntStream::max
to find it, and then filter the map to find all entries matching that value:
var map = Stream.of('A', 'B', 'A', 'B', 'C', 'C', 'A', 'B')
.collect(toMap(w -> w, w -> 1, Integer::sum));
int max = map.values().stream()
.mapToInt(n -> n)
.max().orElse(0);
map.entrySet().stream()
.filter(e -> max == e.getValue())
.forEach(System.out::println);
Output:
A=3
B=3
Upvotes: 5