In C#/.Net it is possible to join IEnumerable sequences with the extension method Enumerable.Join in a SQL 'JOIN ... ON' way.
Is there something similar in Java 8 (Stream API)? Or what is the best way to simulate Enumerable.Join?
解决方案
I haven't found any existing equivalent, but the below method should work:
public static Stream join(
Stream outer, Stream inner,
Function outerKeyFunc,
Function innerKeyFunc,
BiFunction resultFunc) {
//Collect the Inner values into a list as we'll need them repeatedly
List innerList = inner.collect(Collectors.toList());
//matches will store the matches between inner and outer
final Map> matches = new HashMap<>();
//results will be used to collect the results in
final List results = new ArrayList<>();
outer.forEach(o -> innerList
.stream()
//Filter to get those Inners for which the Key equals the Key of this Outer
.filter(i -> innerKeyFunc.apply(i).equals(outerKeyFunc.apply(o)))
.forEach(i -> {
if (matches.containsKey(o)) {
//This Outer already had matches, so add this Inner to the List
matches.get(o).add(i);
} else {
//This is the first Inner to match this Outer, so create a List
List list = new ArrayList<>();
list.add(i);
matches.put(o, list);
}
}));
matches.forEach((out, in) -> in.stream()
//Map each (Outer, Inner) pair to the appropriate Result...
.map(i -> resultFunc.apply(out, i))
//...and collect them
.forEach(res -> results.add(res)));
//Return the result as a Stream, like the .NET method does (IEnumerable)
return results.stream();
}
I only did a brief test of the code using the following inputs:
public static void main(String[] args) {
Stream strings = Arrays.asList("a", "b", "c", "e", "f", "d").stream();
Stream ints = Arrays.asList(1, 2, 3, 6, 5, 4).stream();
Stream results = join(strings, ints,
Function.identity(),
str -> Integer.parseInt(str, 16) - 9,
(o, i) -> "Outer: " + o + ", Inner: " + i);
results.forEach(r -> System.out.println(r));
}
The ints are their own keys, so no transformation
The Strings are mapped to ints according to their hex value - 9
(The elements match if the int values are equal, as per default)
Matching pairs are put into a String
The following (correct) results are printed:
Outer: a, Inner: 1
Outer: b, Inner: 2
Outer: c, Inner: 3
Outer: d, Inner: 4
Outer: e, Inner: 5
Outer: f, Inner: 6
More in-depth testing will be needed, of course, but I believe this implementation to be correct. It could probably be more efficient as well, I'm open to suggestions.