首页 > 解决方案 > 如何使用流口水对列表中的元素进行分组

问题描述

我是 Drools 的新手,我正在努力寻找以下问题的解决方案:

我有一个 Account 类的列表:

class Account {
    private String name;
    private float amount;
}

我想在 drools 中使用名称对帐户列表进行分组。例如:

Account a = new Account("Science", 100);
Account b = new Account("Science", 200);
List<Account> list = new ArrayList<String>();
list.add(a);
list.add(b);

现在我需要一个流口水规则,它应该使用名称对列表中的元素进行分组,并提供具有“科学,300”的列表。

请建议。

标签: javaspring-bootdrools

解决方案


正如 Ironluca 在评论中指出的那样,您使用了错误的工具来完成这项工作。正确的解决方案是使用 Java 流;即使是一个简单的 for 循环也可以解决您的问题。

当然,由于您可以在螺丝上使用锤子,因此您可以使用 Drools 来做到这一点。我将包含一个示例规则和一个解释,尽管它比基于流的解决方案(甚至是 for 循环)效率低得多

我将假设这个模型具有适当的 getter、setter 和全参数构造函数:

class Account {
  String name;
  int quantity;
}

然后,此规则将使用“分组”帐户填充“输出”列表。我已将此“输出”列表声明为全局列表,这不是最佳实践,但无论如何都不建议这样做,因为它不是适合这项工作的工具。

global List $output;

rule "Consolidate duplicates"
when
  $inputs: List()
  Account( $name: name ) from $inputs
  $duplicates: List() 
               from collect( Account( name == $name ) from $inputs )
  $total: Number()
          from accumulate( Account( $value: quantity ) from $duplicates, sum( $value ) )
then
  $output.add(new Account($name, $total));
end

规则的第一部分标识输入列表中共享相同名称的 Account 实例的子集。我使用collect它是因为这是原始输入的一个简单子集。下一步是用来accumulate总结这些重复项的数量。然后我们在规则的 RHS 上的输出列表中添加一个新实例,其中包含合并数量和共享名称。请注意,对于具有唯一名称的帐户,该规则同样适用;的长度没有限制$duplicates,累积只是一个确定的数量。

$inputs就地修改也是可行的,但很棘手,因为您必须担心并发修改异常。更清洁的实现是使用


当然,这比它必须要复杂得多。Java 8 流提供了一个groupingBy收集器,它允许您通过对象内的属性轻松创建映射:

Map<String, List<Account>> accountsByName = accounts.stream()
  .collect(groupingBy(Account::getName));

然后,您可以通过迭代值集轻松地转换它。

这个另一个问题详细介绍了其他“流中重复项的总和”场景:Java 8 stream sum entries for duplicate keys


推荐阅读