Преобразование PriorityQueue в карту с использованием потоков - PullRequest
5 голосов
/ 01 мая 2020

У меня есть приоритетная очередь целых чисел: PriorityQueue<Integer> pq

Я хочу преобразовать это в карту с этим процессом декларативным способом:

  1. poll должно дать ключ
  2. size должен давать значение

Вот что я пробовал

pq.stream().collect(Collectors.groupingBy(queue::poll, queue::size));

Но это не работает, потому что ссылки на методы не Функциональные интерфейсы / Коллекционеры.

Так что я ищу способ, чтобы это произошло.

Я знаю, что мог бы просто использовать al oop и поместить записи в карту, но я собираюсь использовать здесь декларативный стиль.

Ответы [ 4 ]

3 голосов
/ 02 мая 2020

Ваше требование, указанное в комментариях, упростило понимание требования. У меня есть массив чисел. Затем я хочу сопоставить каждое значение в массиве с целым числом, которое является числом значений МЕНЬШЕ, чем текущее значение в массиве .

Я действительно не верю, что эта операция легко поддается потоковой обработке, но вы можете сделать это в два этапа.

  • Сначала отсортируйте массив и сохраните его в List
int[] arr = {1,10,2,4,5,10,19,19,10};
List<Integer> list = Arrays.stream(arr).sorted().boxed().collect(Collectors.toList());
  • Теперь просто соберите их в карту, используя функцию индекса интерфейса List, чтобы получить относительное количество элементов меньше, чем текущий элемент.
  • для размещения дубликатов ключей Я сделал поле value списком для хранения дубликатов ключей. Индексы повторяющихся ключей считаются равными
  • Я также указал LinkedHashMap для сохранения отсортированного порядка клавиш
Map<Integer, List<Integer>> map = list
              .stream()
              .collect(Collectors.groupingBy(a->a, 
                         LinkedHashMap::new,
                         Collectors.mapping(b->list.indexOf(b),
                               Collectors.toList())));

map.entrySet().forEach(System.out::println);

Отпечатки

1=[0]
2=[1]
4=[2]
5=[3]
10=[4, 4, 4]
19=[7, 7]

3 голосов
/ 02 мая 2020

У меня есть массив чисел. Затем я хочу сопоставить каждое значение в массиве с целым числом, которое является числом значений МЕНЬШЕ, чем текущее значение в массиве.

Это то, что вы должны были указать в своем вопросе:)


Поскольку вы хотите сделать это функционально, я рекомендую решить его в два этапа:

  1. Построить карту частот из начального массива целых чисел
  2. Использовать карта частот для построения вашей карты результатов

Следующий фрагмент кода выполняет это:

int[] array = { 42, 3, 100, 56, 3, 11 };

NavigableMap<Integer, Long> frequencyMap = Arrays.stream(array).boxed()
    .collect(Collectors.groupingBy(Function.identity(), TreeMap::new,
        Collectors.counting()));

Map<Integer, Long> countMap = frequencyMap.entrySet().stream()
    .collect(Collectors.toMap(Map.Entry::getKey,
        entry -> frequencyMap.headMap(entry.getKey())
            .values().stream().mapToLong(Long::longValue).sum()));

System.out.println(countMap);

Вывод:

{3=0, 100=5, 56=4, 42=3, 11=2}

Если у вас есть PriorityQueue<Integer> вместо int[], вы можете изменить:

Arrays.stream(array).boxed()

на:

queue.stream()

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

1 голос
/ 02 мая 2020

Здесь PriorityQueue собирается на карту, где ключ - это int из самой очереди, а значение - это количество чисел, меньших его:

 int[] array = { 42, 2, 100, 100, 17, 2, 33 };
 List<Integer> ints = Arrays.stream(array).boxed().collect(toList());
 PriorityQueue<Integer> priorityQueue = new PriorityQueue<Integer>(ints);


 TreeMap<Integer, Long> collect1 = priorityQueue.stream()
       .collect(toMap(
               Function.identity(),
               val -> priorityQueue.stream().filter(i -> val > i).count(),
               (l, r) -> l,
               TreeMap::new
               )
       );

output:

{2=0, 17=2, 33=3, 42=4, 100=5}
0 голосов
/ 04 мая 2020

Чтобы напрямую ответить на вопрос, как я его сформулировал, PriorityQueue можно преобразовать в Карту с использованием потоков, например, так:

Map<Integer, Integer> map = pq.stream().collect(ConcurrentHashMap::new, (integerIntegerHashMap, integer) -> integerIntegerHashMap.put(pq.poll(), pq.size()), ConcurrentHashMap::putAll);

Это параллельный пример, но с заменой на HashMap (или любой другой не -concurrent Map) будет работать в тех сценариях ios, которые не требуют параллелизма.

Для метода collect требуется 3 аргумента (документы: Stream.collect ):

  1. Это должен быть поставщик контейнера (в данном случае это ссылка на конструктор для ConcurrentHashMap)
  2. Это должен быть аккумулятор. Он возьмет контейнер и элемент и выполнит некоторую операцию (здесь я не использую элемент, потому что я создаю отображение, основываясь исключительно на состоянии очереди приоритетов).
  3. Это должен быть объединитель , Он возьмет 2 контейнера (как аргумент 1) и объединит их.

Контейнером здесь является ConcurrentHashMap, а сумматором является метод putAll (потому что он принимает коллекцию).


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

Вход [1,2,3] -> Выход [0, 1, 2] Вход [8,4,4] -> Выход [2, 0, 0]

Вот как Я следовал приведенному выше описанию:

int[] nums = ... (this is the input)
PriorityBlockingQueue<Integer> pq = Arrays.stream(nums).parallel().boxed().collect(()->new PriorityBlockingQueue<>(nums.length, Comparator.reverseOrder()), PriorityBlockingQueue::add, PriorityBlockingQueue::addAll);
Map<Integer, Integer> map = pq.stream().collect(ConcurrentHashMap::new, (integerIntegerHashMap, integer) -> integerIntegerHashMap.put(pq.poll(), pq.size()), ConcurrentHashMap::putAll);
int[] out = Arrays.stream(nums).mapToObj(operand -> map.get(operand)).mapToInt(Integer::intValue).toArray();

Я установил контейнер как Максимальное количество одновременных обращений (очередь с параллельным приоритетом с обращенным компаратором).

Затем вытащим числа 1 на 1 , давая им соответствующее отображение на основе pq. Я не думаю, что этот второй шаг может быть выполнен параллельно, потому что 2 poll вызовов могут завершаться sh последовательно, но мы должны гарантировать, что size вызывается непосредственно после каждого poll.

Наконец, итерируйте по входным элементам и получите соответствующее значение из карты, а затем преобразуйте в массив результатов.

...