Java 如何基于重复的父对象合并列表中的子对象

Java 如何基于重复的父对象合并列表中的子对象,java,lambda,java-8,java-stream,collectors,Java,Lambda,Java 8,Java Stream,Collectors,我有两个具有一对多关系的实体,它们通过复合主键连接在一起。因为Spring数据为oracle数据库生成了错误的计数不同查询,所以我使用笛卡尔连接的SQL输出,这会导致子对象的每一行都重复父对象的行。我需要根据复合键找出不同的父对象,然后将父对象的每个子对象添加到列表中,并将其设置为父对象的属性 我能够根据父对象的组合键找到不同的父对象。以下是相关代码 private static <T> Predicate<T> distinctByKeys(Function<?

我有两个具有一对多关系的实体,它们通过复合主键连接在一起。因为Spring数据为oracle数据库生成了错误的计数不同查询,所以我使用笛卡尔连接的SQL输出,这会导致子对象的每一行都重复父对象的行。我需要根据复合键找出不同的父对象,然后将父对象的每个子对象添加到列表中,并将其设置为父对象的属性

我能够根据父对象的组合键找到不同的父对象。以下是相关代码

private static <T> Predicate<T> distinctByKeys(Function<? super T, ?>... keyExtractors)
{
final Map<List<?>, Boolean> seen = new ConcurrentHashMap<>();
return t ->
{
final List<?> keys = Arrays.stream(keyExtractors)
.map(ke -> ke.apply(t))
collect(Collectors.toList());

return seen.putIfAbsent(keys, Boolean.TRUE) == null;
};
}
我需要产生以下输出

Core Java,200, [Page 1, Page 2]
Learning Freemarker,150, [Page 15]
Spring MVC,300 , [Page 16, Page 17]

这方面的任何帮助都将非常有用

一个选项是使用
收集器.toMap()
标题
页面
值作为键。如果发现重复项,则可以合并两个列表:

Collection<Book> result = list.stream()
        .collect(Collectors.toMap(
                b -> Map.entry(b.getTitle(), b.getPages()),
                b -> new Book(b.getTitle(), b.getPages(), b.getList()),
                (b1, b2) -> new Book(b1.getTitle(), b1.getPages(),
                        Stream.concat(b1.getList().stream(), b2.getList().stream()).collect(Collectors.toList())),
                LinkedHashMap::new))
        .values();
第二种方法使用
title
pages
作为键创建地图,并使用
Collectors.flatMapping()
合并所有书籍的列表。之后,将条目映射回Book对象

如果不能使用收集器.flatMapping()请改用收集器.mapping()`:

List<Book> r = list.stream().collect(
        Collectors.groupingBy(b -> Map.entry(b.getTitle(), b.getPages()), LinkedHashMap::new,
                Collectors.mapping(Book::getList, Collectors.toList())))
        .entrySet().stream()
        .map(e -> new Book(e.getKey().getKey(), e.getKey().getValue(), 
                e.getValue().stream().flatMap(Collection::stream).collect(Collectors.toList())))
        .collect(Collectors.toList());

您是否尝试过“集合.分组依据”?他确实帮助了我。但问题仍然存在,因为在实际情况中,我将Spring数据嵌套投影作为返回类型返回。我想Spring数据中嵌套集合的投影无论如何都是有缺陷的。你知道你正在创建五个过时的类吗?
收集器。flatMapping
仅在Java9或更高版本中可用。@RavindraRanwala从@RavindraRanwala的末尾开始,在这种情况下,你可以使用
收集器.mapping()
。更新了我的答案。或者,您可以将
Collectors.flatMapping(b->b.getList().stream(),Collectors.toList())
替换为
Collector.of(ArrayList::new,(l,b)->l.addAll(b.getList()),(l1,l2)->{l1.addAll(l2);返回l1;}
。但我更喜欢
flatMapping
的后端口版本,它可以在将来很容易地重定向到内置版本。只要返回实体对象,解决方案就可以正常工作。但如果返回类型是带有字段子集的Spring投影,则嵌套集合将不可修改,因为Spring投影是基于元组的,因此是不可变的
List<Book> result = list.stream().collect(
        Collectors.groupingBy(b -> Map.entry(b.getTitle(), b.getPages()), LinkedHashMap::new,
                Collectors.flatMapping(b -> b.getList().stream(), Collectors.toList())))
        .entrySet().stream()
        .map(e -> new Book(e.getKey().getKey(), e.getKey().getValue(), e.getValue()))
        .collect(Collectors.toList());
List<Book> r = list.stream().collect(
        Collectors.groupingBy(b -> Map.entry(b.getTitle(), b.getPages()), LinkedHashMap::new,
                Collectors.mapping(Book::getList, Collectors.toList())))
        .entrySet().stream()
        .map(e -> new Book(e.getKey().getKey(), e.getKey().getValue(), 
                e.getValue().stream().flatMap(Collection::stream).collect(Collectors.toList())))
        .collect(Collectors.toList());
Book[title='Core Java', pages=200, list=[Page 1, Page 2]]
Book[title='Learning Freemarker', pages=150, list=[Page 15]]
Book[title='Spring MVC', pages=300, list=[Page 16, Page 17]]