【问题标题】:Java stream merge and outputJava流合并和输出
【发布时间】:2026-01-25 02:00:01
【问题描述】:

我有一个表单对象列表:

public class Child
{
    private Mom mom;
    private Dad dad;
    private String name;
    private int age;
    private boolean isAdopted;
}

我需要将此列表转换为不同数据结构的列表,将具有相同 Mom 和 Dad 键的对象聚合到表单中

public class Family
{
    private Mom mom;
    private Dad dad;
    private Map<String, int> kids;
}

“儿童”地图是所有儿童姓名到年龄的地图。

目前,我做的翻译如下:

public Collection<Family> transform( final Collection<Child> children )
{
    return children.stream()
                   .filter( child -> !child.getIsAdopted() )
                   .collect( ImmutableTable.toImmutableTable( child -> child.getMom(),
                                                              child -> child.getDad(),
                                                              child -> new HashMap<>(child.getName(), child.getAge() ),
                                                              (c1, c2) -> { 
                                                                  c1.getKids().putAll(c2.getKids());
                                                                  return c1;
                                                              } ) )
                   .cellSet()
                   .stream()
                   .map( Table.Cell::getValue)
                   .collect( Collectors.toList() );
}

有没有办法让我做到这一点,而无需在转换为最终列表之前收集到中间表?

【问题讨论】:

    标签: java java-stream guava


    【解决方案1】:

    如果您可以使用momdad 属性定义GroupingKey,则可以将实现简化为:

    @Getter
    @AllArgsConstructor
    class GroupingKey {
        Mom mom;
        Dad dad;
    }
    
    public List<Family> transformer( final Collection<Child> children ) {
        return children.stream()
                .collect(Collectors.collectingAndThen(
                        Collectors.groupingBy(c -> new GroupingKey(c.getMom(), c.getDad())),
                        map -> map.entrySet().stream()
                                .map(e -> new Family(e.getKey().getMom(), e.getKey().getDad(),
                                        e.getValue().stream().collect(Collectors.toMap(Child::getName, Child::getAge))))
                                .collect(Collectors.toList())));
    }
    

    或者如果不定义任何其他类,您可以使用与以下相同的方法来转换对象:

    public List<Family> transform( final Collection<Child> children ) {
        return children.stream()
                .collect(Collectors.collectingAndThen(
                        Collectors.groupingBy(c -> Arrays.asList(c.getMom(), c.getDad())),
                        map -> map.entrySet().stream()
                                .map(e -> new Family(((Mom) ((List) e.getKey()).get(0)), ((Dad) ((List) e.getKey()).get(1)),
                                        e.getValue().stream().collect(Collectors.toMap(Child::getName, Child::getAge))))
                                .collect(Collectors.toList())));
    }
    

    【讨论】:

      【解决方案2】:

      你可以这样做:

      public static Collection<Family> transform( final Collection<Child> children ) {
          Map<Mom, Map<Dad, Family>> families = new HashMap<>();
          for (Child child : children) {
              if (! child.isAdopted()) {
                  families.computeIfAbsent(child.getMom(), k -> new HashMap<>())
                          .computeIfAbsent(child.getDad(), k -> new Family(child.getMom(), child.getDad(), new HashMap<>()))
                          .getKids().put(child.getName(), child.getAge());
              }
          }
          return families.values().stream().flatMap(m -> m.values().stream()).collect(Collectors.toList());
      }
      

      【讨论】: