我应该使用 Java 8 Streams Api 来组合两个集合吗?

2022-09-03 12:31:21

我有这种情况,似乎Java 8 Streams API会有所帮助,但我不完全确定它是如何做到的。

从两个具有不同元素类型的集合中,我想构建第三个集合,其元素是两个集合中所有可能的元素。基本上:

两种不同的元素类型...

public class A {}
public class B {}

一对“一对”的 As 和 B。

public class Pair {
   private A a;
   private B b;

   public Pair(A a, B b){
     this a = a;
     this b = b;
   }
}

使用旧式API制作的“组合”:java.util.Collection

 public Collection<Pair> combine(Collection<A> as, Collection<B> bs){
    Collection<Pair> pairs = new ArrayList();
    foreach(A a: as){
      foreach(B b: bs){
          Pair pair = new Pair(a,b);
          pairs.add(pair);
      }
    }
    return pairs;
 }

生成的对集合中的排序并不重要。因此,可以创建 Pair 的每个实例,并将其并行添加到生成的集合中。我怎样才能做到这一点?

我自己能弄清楚的最好的方法是使用 Streams 版本:foreach

as.foreach(
  a -> {
    bs.foreach(
      b -> {
          Pair pair = new Pair(a,b);
          pairs.add(pair);
      }
  }
);

为了简化起见,这个例子变得微不足道。该类是将两个元素处理为第三个元素(即 a )的示例,将它们添加到 a 只是可变约简的一个示例。Pairjava.util.function.BiFunctionCollection

有没有更优雅的方法来做到这一点?还是更可取的是,在效率方面以更有利可图的方式?类似的东西

BiFunction<A,B,Pair> combinator = Pair::new; //or any other function f(a,b)=c;

Stream<Pair> pairStream = 
  Streams.unknownElegantMethod(as.stream(), bs.stream(), combinator);

答案 1

我希望我没有任何愚蠢的拼写错误,但基本上你可以做的是:

List<Pair> list = as
                  .stream()
                  .flatMap(a -> bs.stream().map (b -> new Pair(a,b)))
                  .collect (Collectors.toList());
  1. 首先创建一个 from 。Stream<A>as
  2. 对于每个实例
    2.1 创建一个
    2.2 映射到每个实例的一对aStream<B>bsb(a,b)
  3. 将所有对平展为单个流。
  4. 最后,我将它们收集到列表中,尽管您可以选择其他集合。

答案 2

如果您愿意使用第三方库,则可以使用 Eclipse Collections 。这将要求你是a和b都是集合。Eclipse Collections具有内置的类型,因此您无需创建它。Sets.cartesianProduct()Pair

public class A {}
public class B {}

public List<Pair<A, B>> combine(Set<A> as, Set<B> bs)
{
    return Sets.cartesianProduct(as, bs).toList();
}

如果你是 a 和 b 不是集合,那么你可以使用 a 和 ,它们等效于 和 on 。CollectionAdapterflatCollectcollectflatMapmapStream

public Collection<Pair<A, B>> combine(Collection<A> as, Collection<B> bs)
{
    MutableCollection<B> adaptB = CollectionAdapter.adapt(bs);
    return CollectionAdapter.adapt(as)
            .flatCollect(a -> adaptB.asLazy().collect(b -> Tuples.pair(a, b)));
}

另一个可能的选项是定义你自己的 。这比其他解决方案更复杂,并且只有在代码中使用几次时才有用。StreamCollectorcartesianProductStreamcartesianProduct

List<Pair<A, B>> pairs = as.stream().collect(cartesianProduct(bs));

public static <T1, T2> Collector<T1, ?, List<Pair<T1, T2>>> 
    cartesianProduct(Collection<T2> other)
{
    return Collector.of(
            ArrayList::new,
            (list, a) -> list.addAll(
                other.stream().map(b -> new Pair(a, b))).collect(Collectors.toList())),
            (list1, list2) ->
            {
                list1.addAll(list2);
                return list1;
            },
            Collector.Characteristics.UNORDERED
    );
}

注意:我是 Eclipse Collections 的提交者。


推荐