我应该使用Java8StreamsAPI来组合两个集合吗?
在这种情况下,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;
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
);
}