Как найти наибольшее число из списка и его появления с помощью лямбды? - PullRequest
2 голосов
/ 07 апреля 2019

Ниже приведен код, который подсчитывает количество вхождений чисел в потоке и возвращает карту с номером в качестве ключа и значением в виде его вхождений в потоке:

Map<Integer, Long> collect = Stream.of(1, 2, 3, 4, 4, 55555, 12)
                .collect(groupingBy(Function.identity(), counting()));

Как ограничить полученную карту только самым большим числом (или числами в случае ничьей)?

Ответы [ 4 ]

0 голосов
/ 09 апреля 2019

Я бы предложил вам использовать MoreCollectors, определенный в StreamEx , или мою библиотеку abacus-util :

int result = Stream.of(1, 2, 12, 3, 4, 4, 55555, 12)
    .collect(MoreCollectors.maxAll(MoreCollectors.countingInt()));

// map result
Map<Integer, Integer> mapResult = Stream.of(1, 2, 12, 3, 4, 4, 55555, 12)
    .collect(maxAll(groupingBy(Function.identity(), countingInt())));

Потому что вы можете: 1) суммировать все самые большие числа, или 2) сопоставить их с чем-то другим, или 3) ... подробнее. Не нужно и не следует писать такого рода конкретные коды только для одного конкретного пользовательского случая. (если вы хотите знать, как это реализовать, просто загрузите исходный код библиотек или декомпилируйте класс. Они опубликованы в Apache License v2)

Обновление . На самом деле я думаю, что, возможно, это неправильный вопрос, если вы говорите о числах, потому что традиционный for-loop намного проще и эффективнее, чем использование лямбда-выражений:

int[] nums = {1, 2, 12, 3, 4, 4, 55555, 12, 55555};
int[] result = {Integer.MIN_VALUE, 0}; // [0] is the largest number if [1] (occurrence) is bigger than 0.

for (int num : nums) {
  if (num > result[0]) {
    result[0] = num;
    result[1] = 1;
  } else if (num == result[0]) {
    result[1]++;
  }
}

System.out.println(result[0] + ": " + result[1]);

если вам нужно перейти с Stream / Lambdas:

int[] result = IntStream.of(nums).collect(() -> new int[] {Integer.MIN_VALUE, 0}, (a, num) -> {
  if (num > a[0]) {
    a[0] = num;
    a[1] = 1;
  } else if (num == a[0]) {
    a[1]++;
  }
}, (a1, a2) -> {
  if (a1[0] == a2[0]) {
    a1[1] += a2[1];
  } else if (a1[0] < a2[0]) {
    a1[1] = a2[1];
  }
});

System.out.println(result[0] + ": " + result[1]);
0 голосов
/ 07 апреля 2019

Я надеюсь, что кто-то может предложить более простое решение:

 List<Entry<Integer, Long>> list =
        List.of(3, 3, 4, 4, 5, 5, 1)
            .stream()
            .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
            .entrySet()
            .stream()
            .sorted(Map.Entry.<Integer, Long>comparingByValue().reversed())
            .collect(Collectors.toList());

    Map<Integer, Long> result = new HashMap<>();
    Iterator<Entry<Integer, Long>> iter = list.iterator();
    Entry<Integer, Long> left;
    Entry<Integer, Long> right = null;
    while (iter.hasNext()) {
        left = iter.next();
        if (right == null) {
            result.put(left.getKey(), left.getValue());
        }
        if (iter.hasNext() && (right = iter.next()).getValue().longValue() == left.getValue()) {
            result.put(right.getKey(), right.getValue());
        } else {
             break;
        }
    }

Итак, сначала соберите их на ту же карту, что у вас уже есть. Затем отсортируйте их по значению; затем выполните итерацию этого результата и получите только тех, которые находятся в самом начале и соответствуют их значениям.

Идея состоит в том, что, поскольку они уже отсортированы по значению: 3 = 2; 4 = 2; 5 = 2; 1 = 1 - нам нужно повторять только до тех пор, пока повторяется 2, как только такого совпадения не будет, мы закончим (потому что они отсортирован и, следовательно, любой следующий элемент и != 2 будет только меньше).

0 голосов
/ 07 апреля 2019

Взгляните на этот простой пример:

public static void getMeNumbersWithHighestFrequence3(int[] numbers, int howMany) {

    Map<Integer, Long> collect = IntStream.of(numbers).boxed().collect(groupingBy(Function.identity(), TreeMap::new, counting())).descendingMap().entrySet().stream()
            .limit(howMany)
            .collect(TreeMap::new, (map, entry) -> map.put(entry.getKey(), entry.getValue()), Map::putAll);

}

Вы также можете каким-то образом искать их, помещая значение фильтра, и оно будет принимать все записи со значениями ключа, превышающими это значение, что-то вроде:

  public static void getMeNumbersWithHighestFrequenceByFilterNumber(int[] numbers, int value) {

        Map<Integer, Long> collect = IntStream.of(numbers).boxed().collect(groupingBy(Function.identity(), TreeMap::new, counting())).descendingMap().headMap(value, true);

    }

Простое использование:


public static void main(String[] args) {
        int[] numbers = {1, 2, 3, 4, 4, 55555, 12};
        getMeNumbersWithHighestFrequence(numbers, 5);
    }
0 голосов
/ 07 апреля 2019

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

Map<Integer, Long> collect = Stream.of(1, 2, 3, 4, 4, 55555, 12)
        .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
        .entrySet().stream()
        .collect(Collectors.groupingBy(Map.Entry::getValue, TreeMap::new, Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)))
        .lastEntry().getValue();

Возвращает список записей, содержащих номер и частоту. В вашем случае это печатает:

{4=2}

Если вы просто хотите получить число вхождений наибольшего числа, вы можете использовать это:

Map.Entry<Integer, Long> collect = Stream.of(1, 2, 3, 4, 4, 55555, 12)
        .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
        .entrySet().stream()
        .max(Map.Entry.comparingByKey())
        .orElse(null);

Какие отпечатки:

55555=1

В последнем случае у вас есть только одна (максимальное значение) возвращаемая запись. Вы также можете использовать TreeMap, чтобы получить максимальное значение, должно иметь лучшую производительность.

...