流分组由一个字段然后合并所有其他

问题描述:

我有流分组的问题。流分组由一个字段然后合并所有其他

List<FAR> listFar = farList.stream().filter(f -> !f.getStatus().equals(ENUM.STATUS.DELETED)) 
      .collect(Collectors.toList()); 
List<HAUL> haulList = listFar.stream().map(f -> f.getHaul()).flatMap(f -> f.stream()) 
      .collect(Collectors.toList()); 

它按物种分组,但一切都很好,但HAUL还有其他属性。

Map<Specie, List<HAUL>> collect = haulList.stream().collect(Collectors.groupingBy(HAUL::getSpecie)); 

属性:

  1. haul.getFishCount();(整数)
  2. haul.getFishWeight();(BigDecimal)的

是否有可能组由HAUL::getSpecie(由种),而且 “合并” 在一起那两个额外的领域,所以我有总计?

例如:我有3个HAUL元素,其中鱼类物种A的重量为50/30/10千克。

我可以按品种分组并且总重量是多少?

+0

你的意思是把这些加在一起? 'getFishWeight'和'getFishCount'?或每个单独的总和? – Eugene

+0

按物种分组,然后通过getFishCount()和getFishWeight()获得HAUL对象 - 所以这两个字段具有每个物种的总和(因为它们被分组)。 – Kefirchiks

+0

一般而言 - 每个分组物种的独立字段。 – Kefirchiks

如果我理解正确的:

haulsList 
     .stream() 
     .collect(Collectors.groupingBy(HAUL::getSpecie, 
       Collectors.collectingAndThen(Collectors.toList(), 
        list -> { 
        int left = list.stream().mapToInt(HAUL::getFishCount).sum(); 
        BigDecimal right = list.stream().map(HAUL::getFishWeight).reduce(BigDecimal.ZERO, (x, y) -> x.add(y)); 
        return new AbstractMap.SimpleEntry<>(left, right); 
        }))); 

有做一个表格:

.stream() 
.collect(Collectors.groupingBy(HAUL::getSpecie, 
       Collectors.summingInt(HAUL::getFishCount))); 

.stream() 
.collect(Collectors.groupingBy(HAUL::getSpecie, 
      Collectors.mapping(HAUL::getFishWeight, Collectors.reducing((x, y) -> x.add(y))))); 

但你不能真正使这些在该法同时。

+0

谢谢您的解决方案! – Kefirchiks

您可以使用mappingreduce例如:

class Foo { int count; double weight; String spice; } 
List<Foo> fooList = Arrays.asList(
    new Foo(1,new BigDecimal(10), "a"), 
    new Foo(2,new BigDecimal(38), "a"), 
    new Foo(5,new BigDecimal(2), "b"), 
    new Foo(4,new BigDecimal(8), "b")); 

Map<String,Optional<BigDecimal>> spieceWithTotalWeight = fooList.stream(). 
     collect(
       groupingBy(
         Foo::getSpice, 
         mapping(
           Foo::getWeight, 
           Collectors.reducing(BigDecimal::add) 
         ) 
       ) 
     ); 
System.out.println(spieceWithTotalWeight); // {a=Optional[48], b=Optional[10]} 

我希望这有助于。

+0

我有Integer(count)和BigDecimal作为权重。 你总结的是什么意思? – Kefirchiks

+0

@Kefirchiks,我已经更新了我的答案。我认为体重有双重类型。我很抱歉的混淆。 –

如果我正确地得到你的问题,你想每个物种的总数count * weight

您可以通过使用Collectors.groupingBy与下游收集器减少了在每个物种的HAUL列表中的haul.getFishCount() * haul.getFishWeight()总和做到这一点:

Map<Specie, BigDecimal> result = haulList.stream() 
    .collect(Collectors.groupingBy(haul -> haul.getSpecie(), 
     Collectors.mapping(haul -> 
       new BigDecimal(haul.getFishCount()).multiply(haul.getFishWeight()), 
      Collectors.reducing(BigDecimal::plus)))); 

这将得到count * weight每个物种的总和。如果你可以将以下方法添加到您的Haul类:

public BigDecimal getTotalWeight() { 
    return new BigDecimal(getFishCount()).multiply(getFishWeight()); 
} 

然后,收集流会更容易和更具可读性:

Map<Specie, BigDecimal> result = haulList.stream() 
    .collect(Collectors.groupingBy(haul -> haul.getSpecie(), 
     Collectors.mapping(haul -> haul.getTotalWeight(), 
      Collectors.reducing(BigDecimal::plus)))); 

编辑:毕竟,似乎你想为每个领域单独的总和...

我会使用Collectors.toMap与此合并功能。下面的代码:

Map<Specie, List<BigDecimal>> result = haulList.stream() 
    .collect(Collectors.toMap(
     haul -> haul.getSpecie(), 
     haul -> Arrays.asList(
      new BigDecimal(haul.getFishCount()), 
      haul.getFishWeight()), 
     (list1, list2) -> { 
      list1.set(0, list1.get(0).plus(list2.get(0))); 
      list1.set(1, list1.get(1).plus(list2.get(1))); 
      return list1; 
     })); 

它使用2个元素的列表,以存储在索引0鱼计数和鱼体重索引1每一个硬币。

+0

适用于繁殖目的。但我只是想分组,然后获得单独元素的总和。 – Kefirchiks

+0

@Kefirchiks我已经更新了答案,以显示总结两个单独的字段。 –