我可以将以下代码减少为一行/两行吗?
DTO dto;
List<DTO> dtos;
List<Integer> list1 = dtos.stream().map(DTO::getFirstId).distinct().collect(Collectors.toList());
List<Integer> list2 = dtos.stream().map(DTO::getSecondId).distinct().collect(Collectors.toList());
List<Integer> reducedId = list1.stream().filter(list2::contains).collect(Collectors.toList());
答案 0 :(得分:8)
使用单个Java 8流不是一个很好的选择。 相反,您应首先创建一个Set,以便您可以执行有效的包含测试。
Set<Integer> secondIds = dtos.stream().map(DTO::getSecondId).collect(Collectors.toSet());
List<Integer> reducedIds = dtos.stream().map(DTO::getFirstId).distinct()
.filter(secondIds::contains).collect(Collectors.toList());
答案 1 :(得分:3)
如果您愿意使用第三方库,则可以使用Eclipse Collections选项。如果您要使用Stream
,则以下内容应使用Collectors2
。
MutableList<Integer> result =
dtos.stream().map(DTO::getFirstId).collect(Collectors2.toSet())
.intersect(dtos.stream().map(DTO::getSecondId).collect(Collectors2.toSet()))
.toList();
Eclipse Collections也有唯一的LazyIterable
类型,可以用来代替Stream
。
LazyIterable<DTO> iterable = LazyIterate.adapt(dtos);
MutableList<Integer> result =
iterable.collect(DTO::getFirstId).toSet()
.intersect(iterable.collect(DTO::getSecondId).toSet())
.toList();
最后,如果你有大量的id,你可能想要使用原始集来避免装箱Integer
对象。您可以按照以下方式再次使用Stream
和Collectors2
:
IntSet second = dtos.stream().collect(
Collectors2.collectInt(DTO::getSecondId, IntSets.mutable::empty));
IntList result = dtos.stream().collect(
Collectors2.collectInt(DTO::getFirstId, IntSets.mutable::empty))
.select(second::contains).toList();
或者您可以按如下方式使用LazyIterable
:
LazyIterable<DTO> iterable = LazyIterate.adapt(dtos);
IntList result =
iterable.collectInt(DTO::getFirstId).toSet()
.select(iterable.collectInt(DTO::getSecondId).toSet()::contains)
.toList();
注意:我是Eclipse Collections的提交者。
答案 2 :(得分:2)
您可以强制它进行一次流操作,但性能会比您现在的性能更差,即具有二次时间复杂度的操作。
更好的方法是:
Set<Integer> set = dtos.stream().map(DTO::getSecondId).collect(Collectors.toSet());
List<Integer> result = dtos.stream().map(DTO::getFirstId)
.distinct().filter(set::contains).collect(Collectors.toList());
// result now contains the common IDs
通过将第二个ID收集到Set
而不是List
,您无需在第一个流操作中使用distinct()
,并避免将线性搜索应用于每个元素在调用contains
时的第二个流操作中。
您通常可以考虑使用Set
来记住唯一ID。使用Set
作为结果类型时,您也可以避免第二个流操作的distinct()
:
Set<Integer> set = dtos.stream().map(DTO::getSecondId).collect(Collectors.toSet());
Set<Integer> result = dtos.stream().map(DTO::getFirstId)
.filter(set::contains).collect(Collectors.toSet());
如果您怀疑有大量重复的ID,并希望在检查其他Set
之前保留整理重复项的行为,您可以使用:
Set<Integer> set = dtos.stream().map(DTO::getSecondId).collect(Collectors.toSet());
Set<Integer> result = dtos.stream().map(DTO::getFirstId)
.collect(Collectors.toCollection(HashSet::new));
result.retainAll(set);
请注意,如果您更喜欢长时间阅读“单行”,则可以在所有变体中内联set
变量。
答案 3 :(得分:1)
我认为你可以做点什么
List<Integer> reducedId = dtos.stream().map(DTO::getFirstId).distinct().filter(
(dtos.stream().map(DTO::getSecondId).distinct().collect(Collectors.toList()))::contains
).collect(Collectors.toList());
没有在我当地测试,但对我来说似乎是合理的:)
答案 4 :(得分:1)
使用StreamEx:
Set<Integer> set = StreamEx.of(dtos).map(DTO::getSecondId).toSet();
List<Integer> result = StreamEx.of(dtos).map(DTO::getFirstId)
.filter(set::contains).distinct().toList();
Set<Integer> set = Stream.of(dtos).map(DTO::getSecondId).toSet();
List<Integer> result = Stream.of(dtos).map(DTO::getFirstId)
.filter(set::contains).distinct().toList();
// OR:
List<Integer> result = Stream.of(dtos).map(DTO::getFirstId)
.intersection(Stream.of(dtos).map(DTO::getSecondId).toSet()).toList();