我有一个产品交易列表。我想找到 中每个产品的最终(最大)productTransactionId 销售List<ProductTransaction>
。因此,我按 ProductId 对其进行分区,并按 ProductTransactionId 进行排序。下面示例中的最终列表List<Integer> (2, 5, 9)
如何做到这一点?我正在尝试使用流和过滤器。
@Data
public class ProductTransaction {
private int productTransactionId;
private int productId;
private Date saleDate;
private BigDecimal amount;
}
Run Code Online (Sandbox Code Playgroud)
产品交易ID | 产品编号 | 发售日期 | 数量 |
---|---|---|---|
1 | 1 | 2019年3月2日 | 5 |
2 | 1 | 2019 年 4 月 1 日 | 9 |
3 | 2 | 2019 年 4 月 1 日 | 2 |
4 | 2 | 2019年8月21日 | 3 |
5 | 2 | 2019年8月21日 | 4 |
6 | 3 | 2019年10月1日 | 2 |
7 | 3 | 2019年10月3日 | 5 |
8 | 3 | 2019年10月3日 | 7 |
9 | 3 | 2019年10月3日 | 8 |
(请忽略SaleDate,仅按ProductTransactionId排序;表输入数据,不一定已排序
目前使用Java 8
试图:
当前的长解决方案(想要更清晰的速记,或者更快的性能)
Set<Long> finalProductTransactionIds = new HashSet<>();
Set<Long> distinctProductIds = productTransactions.stream()
.map(ProductTransaction::getProductid)
.collect(Collectors.toSet());
for (Long productId: distinctProductIds) {
Long productTransactionId = productTransactions.stream()
.filter(x -> x.getProductId() == productId])
.sorted(Comparator.comparing(ProductTransaction::getProductTransactionId)
.reversed())
.collect(Collectors.toList()).get(0).getProductTransactionId();
finalProductTransactionIds.add(productTransactionId);
}
Run Code Online (Sandbox Code Playgroud)
如果您不介意展开选项,您可以按产品 ID 进行分组,然后使用映射 + maxBy 下游收集器。这避免了必须收集到临时列表,因为仅保留最后一项(但为可选实例增加了最小的开销)。
final Map<Integer, Optional<Integer>> map = transactions.stream()
.collect(
Collectors.groupingBy(
ProductTransaction::getProductId,
Collectors.mapping(
ProductTransaction::getProductTransactionId,
Collectors.maxBy(Comparator.naturalOrder()))));
final Collection<Optional<Integer>> optionalMax = map.values();
final List<Optional<Integer>> max = optionalMax.stream()
.filter(Optional::isPresent)
.collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)
还可以使用toMap
收集器的特殊重载来避免可选类型:
final Collection<Integer> maxTransactionIds = transactions.stream()
.collect(
Collectors.toMap(
ProductTransaction::getProductId,
ProductTransaction::getProductTransactionId,
BinaryOperator.maxBy(Comparator.naturalOrder())))
.values();
Run Code Online (Sandbox Code Playgroud)
感谢Eritrean指出getProductId
返回一个 int,因此我们可以BinaryOperator.maxBy(Comparator.naturalOrder)
用更短的Math::max
( Math#max(int,int)
) 方法引用替换通常适用的方法,它将返回两个整数中较大的值:
final Collection<Integer> maxTransactionIds = transactions.stream()
.collect(
Collectors.toMap(
ProductTransaction::getProductId,
ProductTransaction::getProductTransactionId,
Math::max))
.values();
Run Code Online (Sandbox Code Playgroud)
也许您不喜欢 Stream API。您可以使用常规循环和Map#merge
函数来实现相同的最终结果。如果你仔细观察,合并调用甚至看起来像toMap
收集器(为什么会这样,留给读者作为练习:))。
final Map<Integer, Integer> maxTxPerProduct = new HashMap<>();
for (final ProductTransaction transaction : transactions) {
maxTxPerProduct.merge(
transaction.getProductId(),
transaction.getProductTransactionId(),
Math::max);
}
final Collection<Integer> max = maxTxPerProduct.values();
Run Code Online (Sandbox Code Playgroud)
它绝对避免创建流和收集器对象(无论如何,这很少是问题)。
归档时间: |
|
查看次数: |
393 次 |
最近记录: |