使用Streams将PriorityQueue转换为Map。

问题描述 投票:2回答:2

我有一个Integer的优先队列。PriorityQueue<Integer> pq

我想用声明的方式将其转换为一个映射。

  1. poll 应该给钥匙
  2. size 应给

下面是我试过的

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

但这是行不通的,因为方法引用不是功能接口Collectors。

所以我在寻找一种方法来实现这个功能。

我知道我可以直接使用一个循环,然后把条目放到地图中,但我希望在这里使用声明式的方式。

java java-stream declarative
2个回答
3
投票

我有一个数字数组。然后,我想将数组中的每个值映射到一个整数,这个整数是小于数组中当前值的值的数量。

这是你应该在问题中说明的 :)


因为你想从函数上解决这个问题,我建议分两步解决。

  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()

不管上面的代码段如何,我都建议用必要的方式来解决这个问题,而不是用功能来解决;这样会更容易读懂,你也能进一步优化它。


3
投票

你在评论中陈述的需求让需求更容易理解。 我有一个数字数组。然后,我想将数组中的每个值映射到一个整数上,这个整数是小于数组中当前值的值的数目。.

我真的不相信这个操作很容易适合流处理,但你可以分两步来做。

  • 首先,对数组进行排序,并将其存储在一个叫 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]


1
投票

这里的 PriorityQueue 被收集到一个映射,其中key是队列本身的int,value是比它小的数字量。

 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
               )
       );

输出。

{2=0, 17=2, 33=3, 42=4, 100=5}

0
投票

直接回答我说的问题,一个PriorityQueue可以用流这样转换为Map。

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

这是一个并发的例子,但是用HashMap(或者其他任何非并发的Map)来代替,在那些不需要并发的场景中也可以用。

这个 collect 方法需要3个参数(docs.Stream.collect)。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个个拉出来,根据pq给它们相应的映射。我认为这第二步不能并行完成,因为2 poll 调用可能会连续完成,但我们需要保证的就是 size 后直接调用。poll.

最后,对输入元素进行迭代,并从地图中获取相关的值,然后转换为结果数组。

© www.soinside.com 2019 - 2024. All rights reserved.