Получение максимального числа каждого отдельного числа из HashMap <Integer, List <Integer>> - PullRequest
2 голосов
/ 12 июня 2019

Я имел дело с упражнением, в котором я должен найти решение для следующей проблемы: у меня есть

HashMap<Integer, List<Integer>>

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

25, [30,30,2]

7, [2, 2, 2]

8, [8, 30]

В этом случае я должен каким-то образом получить список со следующими элементами: [2, 2, 2, 8, 30, 30], потому что максимальные вхождения каждого из чисел:

  • в случае 2 это 3 (в значении 7)
  • в случае 8 это 1 (в значении 8)
  • в случае 30 это 2 (в значении 25)

Я пытаюсь использовать для этого потоки, и я написал следующий код:

map.entrySet()
            .stream()
            .map(Map.Entry::getValue)
            .collect(Collectors.toList());

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

Пожалуйста, спросите меня, если вам нужны пояснения.

Заранее спасибо за помощь.

Ответы [ 3 ]

2 голосов
/ 12 июня 2019

Вот быстрое решение с использованием потоков:

public static void main(String[] args) {
    Map<Integer, List<Integer>> map = new HashMap<>();
    map.put(25, Arrays.asList(30, 30, 2));
    map.put(7, Arrays.asList(2, 2, 2));
    map.put(8, Arrays.asList(8, 30));

    List<Integer> result = map.values().stream() // Stream<List<Integer>>
            .map(e -> e.stream().collect(Collectors.groupingBy(p -> p, Collectors.counting()))) // Stream<Map<Integer,Long>>
            .flatMap(m -> m.entrySet().stream()) // Stream<Entry<Integer,Long>>
            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, Long::max)) // Map<Integer,Long>
            .entrySet().stream() // Stream<Entry<Integer,Long>>
            .flatMap(e -> repeatElement(e.getKey(), e.getValue())) // Stream<Integer>
            .collect(Collectors.toList());

    System.out.println(result);
}

private static Stream<Integer> repeatElement(Integer key, Long value) {
    return Stream.generate(() -> key)
            .limit(value);
}

Вывод

[2, 2, 2, 8, 30, 30]
2 голосов
/ 12 июня 2019

Вы можете просто использовать это:

List<Integer> result = map.values().stream()
        .flatMap(list -> list.stream().collect(Collectors.groupingBy(i -> i, Collectors.summingInt(i -> 1))).entrySet().stream())
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, Integer::max)).entrySet().stream()
        .flatMap(e -> Collections.nCopies(e.getValue(), e.getKey()).stream())
        .collect(Collectors.toList());

Сначала вы используете flatMap() с groupingBy(), чтобы получить количество чисел.Затем вы собираете все, чтобы сделать числа (Entry.getKey()) различимыми, используя максимум вхождений (Entry.getValue()).Наконец, вы можете использовать от Collections.nCopies() до flatMap() списка с размером ваших максимальных вхождений.

Результат будет:

[2, 2, 2, 8, 30, 30]
2 голосов
/ 12 июня 2019

Вы можете это так:

static List<Integer> maxOccurrence(Map<Integer, List<Integer>> input) {
    return input.values().stream()
        .flatMap(list -> list.stream().collect(Collectors.groupingBy(Function.identity(), Collectors.counting())).entrySet().stream())
        .collect(Collectors.groupingBy(Map.Entry::getKey, Collectors.mapping(Map.Entry::getValue, Collectors.maxBy(Comparator.naturalOrder()))))
        .entrySet().stream().sorted(Map.Entry.comparingByKey())
        .flatMap(e -> LongStream.range(0, e.getValue().get()).mapToObj(x -> e.getKey()))
        .collect(Collectors.toList());
}

Тест

System.out.println(maxOccurrence(Map.of(
        25, List.of(30, 30, 2),
        7, List.of(2, 2, 2),
        8, List.of(8, 30))));

выход

[2, 2, 2, 8, 30, 30]

Разъяснения

  1. Потоковые значения, то есть списки:

    input.values().stream()
    
    [8, 30]
    [2, 2, 2]
    [30, 30, 2]
    
  2. Подсчитать вхождения каждого значения в каждом списке:

    .flatMap(list -> list.stream().collect(Collectors.groupingBy(Function.identity(), Collectors.counting())).entrySet().stream())
    
    8=1
    30=1
    2=3
    30=2
    2=1
    
  3. Для каждого числа выберите наибольшее вхождение:

    .collect(Collectors.groupingBy(Map.Entry::getKey, Collectors.mapping(Map.Entry::getValue, Collectors.maxBy(Comparator.naturalOrder()))))
    
    {8=1, 2=3, 30=2}
    
  4. Поток, отсортированный по номеру (ключу):

    .entrySet().stream().sorted(Map.Entry.comparingByKey())
    
    2=3
    8=1
    30=2
    
  5. Повтор каждого номера по вхождению:

    .flatMap(e -> LongStream.range(0, e.getValue().get()).mapToObj(x -> e.getKey()))
    
    2
    2
    2
    8
    30
    30
    
  6. Построить список с результатом:

    .collect(Collectors.toList())
    
    [2, 2, 2, 8, 30, 30]
    
...