如何根据重复的父对象合并列表中的子对象
How to merge child objects in list based on duplicate parent object
我有两个具有一对多关系的实体,它们通过复合主键连接在一起。由于 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;
};
}
假设我有以下输入
list.add(new Book("Core Java", 200, new ArrayList(){{add("Page 1");}}));
list.add(new Book("Core Java", 200, new ArrayList(){{add("Page 2");}}));
list.add(new Book("Learning Freemarker", 150, new ArrayList(){{add("Page 15");}}));
list.add(new Book("Spring MVC", 300, new ArrayList(){{add("Page 16");}}));
list.add(new Book("Spring MVC", 300, new ArrayList(){{add("Page 17");}}));
我需要产生以下输出
Core Java,200, [Page 1, Page 2]
Learning Freemarker,150, [Page 15]
Spring MVC,300 , [Page 16, Page 17]
这方面的任何帮助都会非常有帮助
一个选项是使用 Collectors.toMap()
,使用 title
和 pages
值作为键。如果您发现重复项,您可以合并两个列表:
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();
或者您可以使用 Collectors.groupingBy()
:
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());
第二种方法使用 title
和 pages
作为键创建一个 Map,并使用 Collectors.flatMapping()
合并所有书籍的列表。之后,您将条目映射回您的 Book 对象。
如果您不能使用 Collectors.flatMapping()
,请改用 Collectors.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());
如果您不能使用 Map.entry()
,请使用 new AbstractMap.SimpleEntry<>()
。
两种情况下的结果都是这样的:
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]]
我有两个具有一对多关系的实体,它们通过复合主键连接在一起。由于 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;
};
}
假设我有以下输入
list.add(new Book("Core Java", 200, new ArrayList(){{add("Page 1");}}));
list.add(new Book("Core Java", 200, new ArrayList(){{add("Page 2");}}));
list.add(new Book("Learning Freemarker", 150, new ArrayList(){{add("Page 15");}}));
list.add(new Book("Spring MVC", 300, new ArrayList(){{add("Page 16");}}));
list.add(new Book("Spring MVC", 300, new ArrayList(){{add("Page 17");}}));
我需要产生以下输出
Core Java,200, [Page 1, Page 2]
Learning Freemarker,150, [Page 15]
Spring MVC,300 , [Page 16, Page 17]
这方面的任何帮助都会非常有帮助
一个选项是使用 Collectors.toMap()
,使用 title
和 pages
值作为键。如果您发现重复项,您可以合并两个列表:
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();
或者您可以使用 Collectors.groupingBy()
:
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());
第二种方法使用 title
和 pages
作为键创建一个 Map,并使用 Collectors.flatMapping()
合并所有书籍的列表。之后,您将条目映射回您的 Book 对象。
如果您不能使用 Collectors.flatMapping()
,请改用 Collectors.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());
如果您不能使用 Map.entry()
,请使用 new AbstractMap.SimpleEntry<>()
。
两种情况下的结果都是这样的:
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]]