我应该使用Java8StreamsAPI来组合两个集合吗?

我应该使用Java8StreamsAPI来组合两个集合吗?,java,lambda,java-8,java-stream,Java,Lambda,Java 8,Java Stream,在这种情况下,Java8StreamsAPI似乎会有所帮助,但我不完全确定它是如何做到的 从两个具有不同元素类型的集合中,我想构建第三个集合,其元素是两个集合中所有可能的元素对。基本上: 两种不同的元素类型 public class A {} public class B {} As和Bs的“一对” public class Pair { private A a; private B b; public Pair(A a, B b){ this a = a;

在这种情况下,Java8StreamsAPI似乎会有所帮助,但我不完全确定它是如何做到的

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

两种不同的元素类型

public class A {}
public class B {}
As和Bs的“一对”

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

   public Pair(A a, B b){
     this a = a;
     this b = b;
   }
}
使用旧式
java.util.Collection
API进行的“组合”:

 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
是将两个元素处理为第三个元素(即
java.util.function.BiFunction
)的示例,将它们添加到
集合中
只是可变缩减的示例

有没有更优雅的方法?或者更可取的是,在效率方面更有利可图?差不多

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);
BiFunction combinator=Pair::new//或任何其他函数f(a,b)=c;
流对流=
Streams.unknownElegantMethod(as.stream(),bs.stream(),combinator);

我希望我没有任何愚蠢的打字错误,但基本上你能做的是:

List<Pair> list = as
                  .stream()
                  .flatMap(a -> bs.stream().map (b -> new Pair(a,b)))
                  .collect (Collectors.toList());
List=as
.stream()
.flatMap(a->bs.stream().map(b->新对(a,b)))
.collect(Collectors.toList());
  • 首先,从
    创建
    ,作为
  • 对于每个
    a
    实例
    2.1创建
    bs的
    
    2.2将每个
    b
    映射到一对
    (a,b)
  • 将所有对展平为单个流
  • 最后,我将它们收集到一个列表中,不过您可以选择其他集合

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

    public class A {}
    public class B {}
    
    public List<Pair<A, B>> combine(Set<A> as, Set<B> bs)
    {
        return Sets.cartesianProduct(as, bs).toList();
    }
    
    使用
    Stream
    的另一个可能选项是为
    cartesianProduct
    定义自己的
    收集器。这比其他
    解决方案更复杂,只有在代码中多次使用
    cartesianProduct
    时才有用

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

    Listtypo:bs,而不是b,是b的集合的名称。SE不允许我进行这种有用的编辑:(编译时,我得到了“类型不匹配:无法从列表转换到列表”。flatMap似乎没有从lambda中获得正确的类型。我用
    List List List=as.stream().flatMap(a->bs.stream().map(b->new Pair(a,b)).collector(collector.toList()).stream()).collector(collector.toList()))
    但修复似乎有点临时,因为它将流转换为集合,然后再转换回流。这称为“笛卡尔”或“交叉”连接与问题无关,而是
    (a,b)->{返回新的对(a,b)};使用
    Pair::new
    。对于这个问题,流只可用一次。您的Java代码表明您不止一次使用一个流(在
    bs`)上的流)关于
    Pair:new
    ,我已经更新了问题。第二点是错误的,因为
    bs
    集合,而不是流。不,我明白。我的意思是,当你用完流时,你就不能再使用它了。所以解决方案不能或永远不会是
    stream pairStream=Streams。unknownElegantMethod(如.stream(),bs.stream(),combinator);
    当且仅当您想要所有可能的(A,B)对时。使用
    flatMap
    (如答案中所示)或
    foreach
    (如您的代码中所示)是唯一的解决方案。
    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)));
    }
    
    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
        );
    }