Java 8流唯一的整数

时间:2017-02-24 13:43:33

标签: java java-8 java-stream

我可以将以下代码减少为一行/两行吗?

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());

5 个答案:

答案 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对象。您可以按照以下方式再次使用StreamCollectors2

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();

AbacusUtil

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();