将复杂对象收集到一个lambda表达式中

3
我有一个对象列表。首先,我需要按类型对其进行排序。然后按面值排序。最后,总结所有数量:
      class Coin{
            String type;
            BigInteger faceValue;
            BigInteger quantity;
...
       }

            List<Coin> coins = new ArrayList<>();
            coins.add(new Coin("USD", 1, 150));
            coins.add(new Coin("USD", 1, 6));
            coins.add(new Coin("USD", 1, 60));
            coins.add(new Coin("USD", 2, 100));
            coins.add(new Coin("USD", 2, 100));
            coins.add(new Coin("CAD", 1, 111));
            coins.add(new Coin("CAD", 1, 222));

结果列表只能包含3个新的币对象:

Coin("USD", 1 , 216)
Coin("USD", 2 , 200)
Coin("CAD", 1 , 333)

这个如何只用一个 lambda 表达式来编写?

2
当你说“一个lambda表达式”时,你是真的指一个lambda表达式,还是指一个调用链,其中可能包括_多个_ lambda(和方法引用等)? - BeUndead
你需要坚持这个设计(在我看来根本不是最优的),还是如果你想要的话可以改变类的设计? - Tim Biegeleisen
是的,它可能包含多个 lambda。 - user3756506
1个回答

1

你可以使用 Collectors.toMap 来解决这个问题,代码如下:

public List<Coin> groupedCoins(List<Coin> coins) {
    return new ArrayList<>(
            coins.stream()
                    .collect(Collectors.toMap(
                            coin -> Arrays.asList(coin.getType(), coin.getFaceValue()), Function.identity(),
                            (coin1, coin2) -> {
                                BigInteger netQ = coin1.getQuantity().add(coin2.getQuantity());
                                return new Coin(coin1.getType(), coin1.getFaceValue(), netQ);
                            }))
                    .values());
}

或者更复杂的一行代码,将其分组并求和:

public List<Coin> groupedAndSummedCoins(List<Coin> coins) {
    return coins.stream()
            .collect(Collectors.groupingBy(Coin::getType,
                    Collectors.groupingBy(Coin::getFaceValue,
                            Collectors.reducing(BigInteger.ZERO, Coin::getQuantity, BigInteger::add))))
            .entrySet()
            .stream()
            .flatMap(e -> e.getValue().entrySet().stream()
                    .map(a -> new Coin(e.getKey(), a.getKey(), a.getValue())))
            .collect(Collectors.toList());
}

2
为什么人们在按多个值分组时,第一时间想到的是使用可能含糊不清、效率低下的字符串呢?与其使用 coin -> String.format("%s-%s", coin.getType(), coin.getFaceValue()),你可以简单地使用 coin -> Arrays.asList(coin.getType(), coin.getFaceValue()) - Holger
谢谢!这就是我需要的东西! - user3756506
@Holger 我承认我看到很多人使用过这种方法,并且在其他地方也读到过你提出类似的代码建议,但是只使用一个键来进行命名空间划分确实更加自然。我理解这可能会产生歧义,并会尝试在未来的使用中避免这种情况。感谢你的提醒。 :) - Naman
2
除了可能存在歧义之外,连接操作还需要将每个对象转换为字符串,然后进行字符串连接操作,每个操作都可能很昂贵。此外,字符串比较也不便宜。相比之下,List只保留对原始对象的引用,并实现了足够的hashCodeequals来表示一系列对象。字符串方法可能很诱人,因为它看起来很简单,隐藏了所有相关成本。 - Holger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接