Java - 使用stream + lambdas交叉多个集合

Jot*_*edo 5 java collections lambda java-8 java-stream

我有以下函数来统一多个集合(包括重复的元素):

public static <T> List<T> unify(Collection<T>... collections) {
        return Arrays.stream(collections)
               .flatMap(Collection::stream)
               .collect(Collectors.toList()); 
}
Run Code Online (Sandbox Code Playgroud)

如果函数具有类似的集合交集(使用类型相等),那将是很好的.例如:

public static <T> List<T> intersect(Collection<T>... collections) {
     //Here is where the magic happens
}
Run Code Online (Sandbox Code Playgroud)

我发现了交叉函数的一个实现,但它不使用流:

public static <T> Set<T> intersect(Collection<? extends Collection<T>> collections) {
    Set<T> common = new LinkedHashSet<T>();
    if (!collections.isEmpty()) {
       Iterator<? extends Collection<T>> iterator = collections.iterator();
       common.addAll(iterator.next());
       while (iterator.hasNext()) {
          common.retainAll(iterator.next());
       }
    }
    return common;
}
Run Code Online (Sandbox Code Playgroud)

有没有办法实现类似于使用流的统一函数?我在java8/stream api中没那么有经验,因为一些建议会非常有用.

Tag*_*eev 10

您可以在某个实用程序类中编写自己的收集器并使用它:

public static <T, S extends Collection<T>> Collector<S, ?, Set<T>> intersecting() {
    class Acc {
        Set<T> result;

        void accept(S s) {
            if(result == null) result = new HashSet<>(s);
            else result.retainAll(s);
        }

        Acc combine(Acc other) {
            if(result == null) return other;
            if(other.result != null) result.retainAll(other.result);
            return this;
        }
    }
    return Collector.of(Acc::new, Acc::accept, Acc::combine, 
                        acc -> acc.result == null ? Collections.emptySet() : acc.result, 
                        Collector.Characteristics.UNORDERED);
}
Run Code Online (Sandbox Code Playgroud)

用法非常简单:

Set<T> result = Arrays.stream(collections).collect(MyCollectors.intersecting());
Run Code Online (Sandbox Code Playgroud)

但请注意,收集器不能短路:即使中间结果是空集合,它仍将处理流的其余部分.

这样的收集器可以在我的免费StreamEx库中找到(参见参考资料MoreCollectors.intersecting()).它适用于像上面这样的普通流,但如果你将它与StreamEx(扩展普通流)一起使用,它就会变成短路:处理实际上可能会提前停止.