【问题标题】:How to limit the number of elements returned by Stream API expression如何限制 Stream API 表达式返回的元素数量
【发布时间】:2020-08-30 20:34:23
【问题描述】:

我正在使用下面的表达式,来实现以下目标:

在具有相同name::String 的Product 对象之间,提取具有最高weight::BigDecimal 值的产品。

我想通过添加到这个表达式来限制返回 Collection 的长度,但我不确定如何做到这一点 - 具体来说,我想用最多 3 个 Product 对象填充列表,理想情况下,最重的那些。

Collection<Product> heaviestProductsDistinct = products.stream()
    .collect(Collectors.toMap(Product::getName, b -> b,
        BinaryOperator.maxBy(Comparator.comparing(Product::getPrice))))
    .values();

例如,假设我有:

List<Product> products = List.of(
        new Product("product1", BigDecimal.valueOf(3), "valid"),
        new Product("product1", BigDecimal.valueOf(2), "expired"),
        new Product("product1", BigDecimal.valueOf(5), "expired"),

        new Product("product2", BigDecimal.valueOf(4), "valid"),
        new Product("product2", BigDecimal.valueOf(10), "valid"),

        new Product("product3", BigDecimal.valueOf(9), "expired"),
        new Product("product3", BigDecimal.valueOf(3)), "valid",

        new Product("product4", BigDecimal.valueOf(12), "valid"),
        new Product("product4", BigDecimal.valueOf(33), "valid")
);

我的表情会返回:

[Product{name='product4', price=33, condition='valid'}, Product{name='product2', price=10, condition='valid'}, 
Product{name='product1', price=5, condition='expired'}, Product{name='product3', price=9, condition='valid'}]

因此,我只想保留两个元素,最好是价格最高的元素。

【问题讨论】:

  • 你说的是哪个“返回ArrayList”?您的代码中没有 ArrayList。为什么你说“我想用最多 3 个 Product 对象填充列表”,然后又说“我只想保留两个元素”?

标签: java-8 functional-programming java-stream


【解决方案1】:

你的描述不清楚,所以这里有两个

最贵的 3 种产品,每个名称只选择最贵的产品

final List<Product> collect = products.stream()
    .collect(
        Collectors.groupingBy(
            Product::getName,
            Collectors.maxBy(Comparator.comparing(Product::getPrice))
        )
    )
    .values()
    .stream()
    .filter(Optional::isPresent)
    .map(Optional::get)
    .sorted(Comparator.comparing(Product::getPrice).reversed())
    .limit(3)
    .collect(Collectors.toList());

每个名字最贵的 3 种产品

您可以先按名称分组,然后使用collectingAndThen 获取每个名称的结果列表,对其进行排序,并获得前 3 个。

这会给你一张地图。然后,您可以根据需要迭代并连接所有值。

List<Product> collect = products.stream()
    .collect(
        Collectors.groupingBy(
            Product::getName,
            Collectors.collectingAndThen(
                Collectors.toList(),
                list -> list.stream()
                    .sorted(Comparator.comparing(Product::getPrice).reversed())
                    .limit(3)
                    .collect(Collectors.toList())
            )
        )
    ) // here you have a Map<String, List<Product>>
    .values()
    .stream()
    .flatMap(List::stream)
    .collect(Collectors.toList());

【讨论】:

  • 谢谢,这可行,但它不会返回具有不同名称的产品。首先,如果我有 3 个同名的 Product 对象,我只保留权重最高的那个。因此,我发布的代码返回一个集合,其中包含具有唯一名称的 Product 对象,每个对象也是该名称中最重的 Product。但是从这个返回的集合中,我希望 abe 只保留 N 个结果。
  • 是的,然后从他们那里,能够只保留N个
  • 抱歉问题含糊不清。我添加了一个详细的示例。你帖子的第一部分是回答我问题的部分。谢谢! (最贵的 3 个产品,每个名称只选择最贵的)
  • 为什么将 OP 的代码从 Collectors.toMap(…) 更改为 Collectors.groupingBy(… Collectors.maxBy(…))?您获得的只是之后插入.filter(Optional::isPresent) .map(Optional::get) 的必要性。
  • @Holger,你能把完整的表达出来吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-29
  • 2015-01-18
  • 2019-01-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多