У меня есть поток элементов, и я хочу преобразовать его в другой поток, который будет состоять только из элементов, которые больше всего проявлялись в предыдущем потоке, при условии, что у меня может быть несколько элементов с одинаковым количеством вхождений.
В качестве примера:
Входной поток: ['A', 'B', 'A', 'B', 'C', 'C', 'A', 'B']
Выходной поток: ['A', B '], поскольку они оба имели максимальное количество вхождений в предыдущем потоке, равном 3.
Пока что я написал следующий код:
stream.collect(toMap(w -> w, w -> 1, Integer::sum))
.entrySet()
.stream()
.max(comparing(Map.Entry::getValue))
.map(Map.Entry::getKey);
Но это позволяет мне получить только один из элементов с максимальным количеством вхождений, и для случая выше он даст либо «A», либо «B».
Есть ли какой-нибудь элегантный способ добиться этого с помощью лямбда-выражений?
Спасибо, ура
Кажется, это конкретный случай Как заставить max () возвращать ВСЕ максимальные значения в потоке Java?




Нет простого способа сделать это за один раз, но вы можете отсортировать карту, чтобы найти максимальное значение, или просто использовать IntStream::max, чтобы найти его, а затем отфильтровать карту, чтобы найти все записи, соответствующие этому значению:
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);
Выход:
A=3
B=3
Хороший! хотя я бы использовал .max().orElse(0); или .max().orElse(-1); для безопасности вместо .max().getAsInt();
поскольку вас интересуют только значения, чтобы найти максимальное значение, которое вы могли бы использовать для map.values().stream().mapToInt(Integer::intValue)... вместо потоковой передачи через entrySet. Кроме того, можно также выполнить int max = Collections.max(map.values());, чтобы найти максимальный элемент. Единственная проблема сейчас в том, что если источник пуст, генерируется исключение.
@ Aominè как есть, если исходный код пуст, вы просто не получите вывода, но использование values().stream().mapToInt(n -> n) чище, чем то, что есть у меня. Я так задумался о entrySet(), что даже не подумал о values().
@ Андрей. Этот ответ лучше моего.
Во-первых, почему вы начинаете с Stream<Character>. Вы можете начать с массива символов, а затем использовать итерацию для построения карты от char до count и тем временем вычислить максимальное значение на карте во время итерации. Затем используйте эту карту и промежуточный результат с максимальным значением, чтобы получить окончательный результат. Этот подход просто пропускает сбор только дважды. Вот как это выглядит, учитывая, что у вас есть char[] с именем chars заранее.
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());
Выход:
[A, B]
вам следует использовать Objects.equals вместо ==, так как entry.getValue() является эталонным типом, и поэтому он является результатом max, или вы можете избежать использования Objects.equals с небольшими изменениями, например, используя .entry.getValue().longValue() вместо entry.getValue().
ваш код работает только сейчас, потому что данных в списке chars мало. так что это не гарантия, что он всегда будет работать. см. этот ответ
@OusmaneD. Пожалуйста, посмотрите мою последнюю правку. Спасибо за ответ. Ценить это.
Это не потоковая передача, поскольку вам, по определению, необходимо увидеть последний элемент перед отправкой первого нисходящего потока. Представление о том, что вы транслируете, ничего вам не дает.