[英]What is the Java 8 Stream API equivalent for LINQ Join?
In C#/.Net it is possible to join IEnumerable sequences with the extension method Enumerable.Join in a SQL 'JOIN ... ON' way. 在C#/ .Net中,可以使用扩展方法Enumerable.Join以SQL'JOIN ... ON'方式连接IEnumerable序列。
Is there something similar in Java 8 (Stream API)? Java 8(Stream API)中有类似的东西吗? Or what is the best way to simulate Enumerable.Join?
或者模拟Enumerable.Join的最佳方法是什么?
See: https://msdn.microsoft.com/en-us/library/bb534675%28v=vs.100%29.aspx 请参阅: https : //msdn.microsoft.com/en-us/library/bb534675%28v=vs.100%29.aspx
join is just syntactic sugar for Stream.flatMap()
as explained in this article . join只是
Stream.flatMap()
语法糖,如本文所述 。 Consider this example: 考虑这个例子:
List<Integer> l1 = Arrays.asList(1, 2, 3, 4);
List<Integer> l2 = Arrays.asList(2, 2, 4, 7);
l1.stream()
.flatMap(i1 -> l2.stream()
.filter(i2 -> i1.equals(i2)))
.forEach(System.out::println);
The result is: 结果是:
2
2
4
In the above example, flatMap()
corresponds to (INNER) JOIN
whereas the filter()
operation of the nested stream corresponds to the ON
clause. 在上面的示例中,
flatMap()
对应于(INNER) JOIN
而嵌套流的filter()
操作对应于ON
子句。
jOOλ is a library that implements innerJoin()
and other join types to abstract over this, eg also to buffer stream contents in case you want to join two Stream
instances, instead of two Collection
instances. jOOλ是一个实现
innerJoin()
和其他连接类型的库,用于innerJoin()
进行抽象,例如,如果要连接两个Stream
实例而不是两个Collection
实例,还要缓冲流内容。 With jOOλ, you would then write: 有了jOOλ,你会写:
Seq<Integer> s1 = Seq.of(1, 2, 3, 4);
Seq<Integer> s2 = Seq.of(2, 2, 4, 7);
s1.innerJoin(s2, (i1, i2) -> i1.equals(i2))
.forEach(System.out::println);
... which prints (the output are tuples, which is more like SQL's semantics semantics): ...打印(输出是元组,更像是SQL的语义语义):
(2, 2)
(2, 2)
(4, 4)
(disclaimer, I work for the company behind jOOλ) (免责声明,我为jOOλ背后的公司工作)
I haven't found any existing equivalent, but the below method should work: 我还没有找到任何现有的等价物,但下面的方法应该有效:
public static <Outer, Inner, Key, Result> Stream<Result> join(
Stream<Outer> outer, Stream<Inner> inner,
Function<Outer, Key> outerKeyFunc,
Function<Inner, Key> innerKeyFunc,
BiFunction<Outer, Inner, Result> resultFunc) {
//Collect the Inner values into a list as we'll need them repeatedly
List<Inner> innerList = inner.collect(Collectors.toList());
//matches will store the matches between inner and outer
final Map<Outer, List<Inner>> matches = new HashMap<>();
//results will be used to collect the results in
final List<Result> 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<Inner> 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<String> strings = Arrays.asList("a", "b", "c", "e", "f", "d").stream();
Stream<Integer> ints = Arrays.asList(1, 2, 3, 6, 5, 4).stream();
Stream<String> 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));
}
int
s are their own keys, so no transformation int
是他们自己的键,所以没有转换 Strings
are mapped to int
s according to their hex value - 9 Strings
根据其十六进制值-9映射到int
int
values are equal, as per default) int
值相等,则元素匹配,默认情况下) String
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.
它也可能更有效率,我愿意接受建议。
I also came from C# and missed that feature. 我也来自C#并错过了这个功能。 One big advantage would be to have readable code by expressing intention.
一个很大的优点是通过表达意图来获得可读代码。 So I wrote my own streamjoin which works like C# Enumerable.Join().
所以我编写了自己的streamjoin ,它的工作方式类似于C#Enumerable.Join()。 Plus: it tolerates null-keys.
另外:它容忍空键。
Stream<BestFriends> bestFriends =
join(listOfPersons.stream())
.withKey(Person::getName)
.on(listOfDogs.stream())
.withKey(Dog::getOwnerName)
.combine((person, dog) -> new BestFriends(person, dog))
.asStream();
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.