此问题是帖子的扩展:Java 8 groupingby with returning multiple field。
对于相同的问题,如何返回Customer
的列表?例如,它应该返回:
Customer("A",4500,6500)
Customer("B",3000,3500)
Customer("C",4000,4500)
答案 0 :(得分:3)
使用以下代码:
Map<String, Customer> retObj =
listCust.stream()
.collect(Collectors.groupingBy(Customer::getName,
Collector.of(
Customer::new,
(c1, c2) -> {
c1.setName(c2.getName());
c1.setTotal(c1.getTotal() + c2.getTotal());
c1.setBalance(c1.getBalance() + c2.getBalance());
},
(c3, c4) -> {
c3.setTotal(c3.getTotal() + c4.getTotal());
c3.setBalance(c3.getBalance() + c4.getBalance());
return c3;
})));
System.out.println(retObj);
System.out.println(retObj.values()); //If you want only the list of all Customers
输出:
{
A=Customer [name=A, total=4500.0, balance=6500.0],
B=Customer [name=B, total=3000.0, balance=3500.0],
C=Customer [name=C, total=4000.0, balance=4500.0]
}
答案 1 :(得分:3)
Map<String, Customer>
作为结果集+1,那么 @Pankaj Singhal的帖子是正确的主意。但是,我会将合并逻辑提取到其自己的函数中,例如在Customer
类中,您将具有这样的功能:
public static Customer merge(Customer first, Customer second) {
first.setTotal(first.getTotal() + second.getTotal());
first.setBalance(first.getBalance() + second.getBalance());
return first;
}
然后,流查询将变为:
Map<String, Customer> retObj =
listCust.stream()
.collect(Collectors.toMap(Customer::getName, Function.identity(), Customer::merge));
listCust.stream()
创建一个stream对象,即Stream<Customer>
。collect
对以下元素执行可变归约运算
该流使用提供的Collector
。toMap
的结果是提供的收集器,toMap
方法提取键Customer::getName
和值Function.identity()
,如果映射的键包含重复项,则合并功能{ {1}}用于解决冲突。将合并逻辑提取到其自身的功能中,我看到了三个好处:
但是,如果您打算检索Customer::merge
:
Collection<Customer>
或Collection<Customer> result = listCust.stream()
.collect(Collectors.toMap(Customer::getName,
Function.identity(),
Customer::merge))
.values();
作为结果集,那么您所要做的就是调用List<Customer>
并将其结果传递给values()
构造函数:
ArrayList
更新:
如果您不想更改源中的对象,则只需修改List<Customer> result = new ArrayList<>(listCust.stream()
.collect(Collectors.toMap(Customer::getName,
Function.identity(),
Customer::merge))
.values());
函数,如下所示:
merge
一切都保持不变。
答案 2 :(得分:2)
这里的other answers很不错,但是它们会使输入中的Customer
实例发生突变,这可能是意外的。
为避免这种情况,请使用自定义Collector
。
首先,创建一个返回一个Collector
的{{1}}并将其合并为单个Stream<Customer>
的方法:
Customer
这假设public static Collector<Customer, Customer, Customer> customerCollector() {
return Collector.of(Customer::new, TestBench::merge,
(l, r) -> {
merge(l, r);
return l;
});
}
public static void merge(final Customer first, final Customer second) {
first.setName(second.getName());
first.setTotal(first.getTotal() + second.getTotal());
first.setBalance(first.getBalance() + second.getBalance());
}
具有noargs构造函数。
那么您可以做:
Customer
请注意,我使用Collection<Customer> result = listCust.stream()
.collect(groupingBy(Customer::getName, customerCollector()))
.values();
而不是groupingBy
-toMap
收集器专门用于对元素进行分组。
答案 3 :(得分:0)
这可以做到,
List<Customer> result = customers.stream().collect(Collectors.collectingAndThen(
Collectors.toMap(Customer::getName, c -> Arrays.asList(c.getTotal(), c.getBalance()),
(l1, l2) -> IntStream.range(0, l1.size()).mapToDouble(i -> l1.get(i) + l2.get(i)).boxed()
.collect(Collectors.toList())),
m -> m.entrySet().stream().map(e -> new Customer(e.getKey(), e.getValue().get(0), e.getValue().get(1)))
.collect(Collectors.toList())));
首先创建Map
,然后使用Finisher函数通过List
计算Customer
个实例中的Map.Entry
。