java 8 - 流,映射和计数不同

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/30282700/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 16:49:34  来源:igfitidea点击:

java 8 - stream, map and count distinct

javajava-8java-stream

提问by Zemer

My first attempt with java 8 streams...

我第一次尝试使用 java 8 流......

I have an object Bid, which represents a bid of a user for an item in an auction. i have a list of bids, and i want to make a map that counts in how many (distinct) auctions the user made a bid.

我有一个对象出价,它代表用户对拍卖中物品的出价。我有一个出价列表,我想制作一张地图,计算用户出价的(不同的)拍卖数量。

this is my take on it:

这是我的看法:

bids.stream()
         .collect(
             Collectors.groupingBy(
                  bid ->  Bid::getBidderUserId, 
                  mapping(Bid::getAuctionId, Collectors.toSet())
             )
         ).entrySet().stream().collect(Collectors.toMap(
             e-> e.getKey(),e -> e.getValue().size())
        );

It works, but i feel like i'm cheating, cause i stream the entry sets of the map, instead of doing a manipulation on the initial stream... must be a more correct way of doing this, but i couldn't figure it out...

它有效,但我觉得我在作弊,因为我流式传输地图的入口集,而不是对初始流进行操作......必须是一种更正确的方法,但我想不通出来...

Thanks

谢谢

回答by Tagir Valeev

You can perform groupingBytwice:

您可以执行groupingBy两次:

Map<Integer, Map<Integer, Long>> map = bids.stream().collect(
        groupingBy(Bid::getBidderUserId,
                groupingBy(Bid::getAuctionId, counting())));

This way you have how many bids each user has in each auction. So the size of internal map is the number of auctions the user participated. If you don't need the additional information, you can do this:

通过这种方式,您可以获得每个用户在每次拍卖中的出价数量。所以内部地图的大小就是用户参与的拍卖次数。如果您不需要其他信息,您可以这样做:

Map<Integer, Integer> map = bids.stream().collect(
        groupingBy(
                Bid::getBidderUserId,
                collectingAndThen(
                        groupingBy(Bid::getAuctionId, counting()),
                        Map::size)));

This is exactly what you need: mapping of users to number of auctions user participated.

这正是您所需要的:将用户映射到用户参与的拍卖数量。

Update:there's also similar solution which is closer to your example:

更新:还有类似的解决方案,更接近您的示例:

Map<Integer, Integer> map = bids.stream().collect(
        groupingBy(
                Bid::getBidderUserId,
                collectingAndThen(
                        mapping(Bid::getAuctionId, toSet()),
                        Set::size)));

回答by Ruben

Tagir Valeev's answer is the right one (+1). Here is an additional one that does exactly the same using your own downstream Collector for the groupBy:

Tagir Valeev 的答案是正确的(+1)。这是一个额外的,使用您自己的下游收集器为 groupBy 做完全相同的事情:

    Map<Integer, Long> map = bids.stream().collect(
               Collectors.groupingBy(Bid::getBidderUserId, 
                                     new Collector<Bid, Set<Integer>, Long>() {

        @Override
        public Supplier<Set<Integer>> supplier() {
            return HashSet::new;
        }

        @Override
        public BiConsumer<Set<Integer>, Bid> accumulator() {
            return (s, b) -> s.add(b.getAuctionId());
        }

        @Override
        public BinaryOperator<Set<Integer>> combiner() {
            return (s1, s2) -> {
                s1.addAll(s2);
                return s1;
            };
        }

        @Override
        public Function<Set<Integer>, Long> finisher() {
            return (s) -> Long.valueOf(s.size());
        }

        @Override
        public Set<java.util.stream.Collector.Characteristics> characteristics() {
            return Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.UNORDERED, Collector.Characteristics.IDENTITY_FINISH));
        }
    }));