在C#/ .Net中,可以在SQL' JOIN ... ON'中使用扩展方法Enumerable.Join连接IEnumerable序列。方式。
Java 8(Stream API)中是否有类似内容?或者模拟Enumerable.Join的最佳方法是什么?
请参阅: https://msdn.microsoft.com/en-us/library/bb534675%28v=vs.100%29.aspx
答案 0 :(得分:8)
join is just syntactic sugar for Stream.flatMap()
as explained in this article。考虑这个例子:
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);
结果是:
2
2
4
在上面的示例中,flatMap()
对应(INNER) JOIN
,而嵌套流的filter()
操作对应ON
子句。
jOOλ是一个实现innerJoin()
和其他连接类型的库,用于抽象,例如还要缓冲流内容,以防您想要加入两个Stream
实例,而不是两个Collection
个实例。随着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);
...打印(输出是元组,更像是SQL的语义语义):
(2, 2)
(2, 2)
(4, 4)
(免责声明,我为jOOλ背后的公司工作)
答案 1 :(得分:1)
我还没有找到任何现有的等价物,但下面的方法应该有效:
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();
}
我只使用以下输入对代码进行了简短的测试:
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
是他们自己的密钥,所以没有转换Strings
根据其十六进制值 - 9 int
int
值相等,则元素匹配,默认情况下)String
打印以下(正确)结果:
Outer: a, Inner: 1
Outer: b, Inner: 2
Outer: c, Inner: 3
Outer: d, Inner: 4
Outer: e, Inner: 5
Outer: f, Inner: 6
当然,还需要进行更深入的测试,但我认为这种实施是正确的。它也可能更有效率,我可以接受建议。
答案 2 :(得分:1)
我也是来自C#而错过了这个功能。一个很大的优点是通过表达意图来获得可读代码。所以我写了自己的streamjoin,它的作用类似于C#Enumerable.Join()。另外:它可以容忍空键。
Stream<BestFriends> bestFriends =
join(listOfPersons.stream())
.withKey(Person::getName)
.on(listOfDogs.stream())
.withKey(Dog::getOwnerName)
.combine((person, dog) -> new BestFriends(person, dog))
.asStream();