Java 8 получить элементы с равным максимальным количеством вхождений

У меня есть поток элементов, и я хочу преобразовать его в другой поток, который будет состоять только из элементов, которые больше всего проявлялись в предыдущем потоке, при условии, что у меня может быть несколько элементов с одинаковым количеством вхождений.

В качестве примера:

Входной поток: ['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».

Есть ли какой-нибудь элегантный способ добиться этого с помощью лямбда-выражений?

Спасибо, ура

Это не потоковая передача, поскольку вам, по определению, необходимо увидеть последний элемент перед отправкой первого нисходящего потока. Представление о том, что вы транслируете, ничего вам не дает.

Boris the Spider 02.06.2018 21:14

Кажется, это конкретный случай Как заставить max () возвращать ВСЕ максимальные значения в потоке Java?

Holger 03.06.2018 11:21
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
4
2
1 105
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Нет простого способа сделать это за один раз, но вы можете отсортировать карту, чтобы найти максимальное значение, или просто использовать 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();

Ousmane D. 02.06.2018 20:39

поскольку вас интересуют только значения, чтобы найти максимальное значение, которое вы могли бы использовать для map.values().stream().mapToInt(Integer::intValue)... вместо потоковой передачи через entrySet. Кроме того, можно также выполнить int max = Collections.max(map.values());, чтобы найти максимальный элемент. Единственная проблема сейчас в том, что если источник пуст, генерируется исключение.

Ousmane D. 02.06.2018 20:51

@ Aominè как есть, если исходный код пуст, вы просто не получите вывода, но использование values().stream().mapToInt(n -> n) чище, чем то, что есть у меня. Я так задумался о entrySet(), что даже не подумал о values().

David Conrad 02.06.2018 20:55

@ Андрей. Этот ответ лучше моего.

Oleksandr Pyrohov 02.06.2018 21:37
Ответ принят как подходящий

Во-первых, почему вы начинаете с 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().

Ousmane D. 03.06.2018 13:28

ваш код работает только сейчас, потому что данных в списке chars мало. так что это не гарантия, что он всегда будет работать. см. этот ответ

Ousmane D. 03.06.2018 13:34

@OusmaneD. Пожалуйста, посмотрите мою последнюю правку. Спасибо за ответ. Ценить это.

Ravindra Ranwala 30.03.2020 09:36

Другие вопросы по теме