简体   繁体   中英

Should I use Java 8 Streams Api to combine two Collections?

I have this situation where it seems that the Java 8 Streams API would be helpful, but I'm not fully sure how it could be.

From two Collections with distinct element types, I want to build a third collection whose elements are all the possible Pairs of elements from both collections. Basically:

The two distinct element types...

public class A {}
public class B {}

A "pair" of As and Bs.

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

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

The " combination " made using old-style 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;
 }

The ordering in the resulting pairs collection is not important. So, every instance of Pair could be created and added to the resulting collection in parallel. How could I achieve this?

The best I could figure out by myself was to use the Streams version of foreach :

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

This example was made trivial for the sake of simplification. The class Pair is an example of processing two elements into a third one (that is, a java.util.function.BiFunction ), and adding them to a Collection is just an example of a mutable reduction.

Is there a more elegant way to do that? Or preferable, in a more profitable way in regards to efficiency? Something like

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

I hope I don't have any silly typos, but basically what you can do is :

List<Pair> list = as
                  .stream()
                  .flatMap(a -> bs.stream().map (b -> new Pair(a,b)))
                  .collect (Collectors.toList());
  1. First you create a Stream<A> from as .
  2. For each a instance
    2.1 Create a Stream<B> of bs
    2.2 Map each b to a pair of (a,b)
  3. Flatten all the pairs to a single Stream.
  4. Finally I collected them to a List, though you can choose other collections.

If you're open to using a third-party library, you could use Eclipse Collections Sets.cartesianProduct() . This would require that your a's and b's are both Sets. Eclipse Collections has a Pair type built-in, so you wouldn't need to create it.

public class A {}
public class B {}

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

If your a's and b's are not Sets, then you could use a CollectionAdapter flatCollect and collect , which are equivalent to flatMap and map on Stream .

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

Another possible option using Stream would be to define your own Collector for cartesianProduct . This is more complex than the other Stream solution, and would only be useful if you used cartesianProduct a few times in your code.

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

Note: I am a committer for Eclipse Collections .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM