什么是LINQ Join的Java 8 Stream API等价物?

Tit*_*ium 8 .net c# java linq join

在C#/ .Net中,可以使用扩展方法Enumerable.Join以SQL'JOIN ... ON'方式连接IEnumerable序列.

Java 8(Stream API)中有类似的东西吗?或者模拟Enumerable.Join的最佳方法是什么?

请参阅:https: //msdn.microsoft.com/en-us/library/bb534675%28v=vs.100%29.aspx

Luk*_*der 8

Stream.flatMap()如本文所述,join只是语法糖.考虑这个例子:

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);
Run Code Online (Sandbox Code Playgroud)

结果是:

2
2
4
Run Code Online (Sandbox Code Playgroud)

在上面的示例中,flatMap()对应于嵌套流(INNER) JOINfilter()操作对应于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);
Run Code Online (Sandbox Code Playgroud)

...打印(输出是元组,更像是SQL的语义语义):

(2, 2)
(2, 2)
(4, 4)
Run Code Online (Sandbox Code Playgroud)

(免责声明,我为jOOλ背后的公司工作)


小智 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();
}
Run Code Online (Sandbox Code Playgroud)

我仅使用以下输入对代码进行了简短测试:

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));
}
Run Code Online (Sandbox Code Playgroud)
  • s 是它们int自己的键,因此无需转换
  • 根据其十六进制值Strings映射到s - 9int
  • 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
Run Code Online (Sandbox Code Playgroud)

当然,还需要更深入的测试,但我相信这个实现是正确的。它也可能会更有效,我愿意接受建议。