Enumerables { public static <TSource, TInner, TResult> Enumerable<TResult> thetaJoin( final Enumerable<TSource> outer, final Enumerable<TInner> inner, final Predicate2<TSource, TInner> predicate, Function2<TSource, TInner, TResult> resultSelector, final boolean generateNullsOnLeft, final boolean generateNullsOnRight) { final List<TResult> result = Lists.newArrayList(); final Enumerator<TSource> lefts = outer.enumerator(); final List<TInner> rightList = inner.toList(); final Set<TInner> rightUnmatched; if (generateNullsOnLeft) { rightUnmatched = Sets.newIdentityHashSet(); rightUnmatched.addAll(rightList); } else { rightUnmatched = null; } while (lefts.moveNext()) { int leftMatchCount = 0; final TSource left = lefts.current(); final Enumerator<TInner> rights = Linq4j.iterableEnumerator(rightList); while (rights.moveNext()) { TInner right = rights.current(); if (predicate.apply(left, right)) { ++leftMatchCount; if (rightUnmatched != null) { rightUnmatched.remove(right); } result.add(resultSelector.apply(left, right)); } } if (generateNullsOnRight && leftMatchCount == 0) { result.add(resultSelector.apply(left, null)); } } if (rightUnmatched != null) { final Enumerator<TInner> rights = Linq4j.iterableEnumerator(rightUnmatched); while (rights.moveNext()) { TInner right = rights.current(); result.add(resultSelector.apply(null, right)); } } return Linq4j.asEnumerable(result); } private  Enumerables(); static Enumerable<E> slice0(Enumerable<E[]> enumerable); static Enumerable<TSource> semiJoin(
      final Enumerable<TSource> outer, final Enumerable<TInner> inner,
      final Function1<TSource, TKey> outerKeySelector,
      final Function1<TInner, TKey> innerKeySelector); static Enumerable<TSource> semiJoin(
      final Enumerable<TSource> outer, final Enumerable<TInner> inner,
      final Function1<TSource, TKey> outerKeySelector,
      final Function1<TInner, TKey> innerKeySelector,
      final EqualityComparer<TKey> comparer); static Enumerable<TResult> thetaJoin(
      final Enumerable<TSource> outer, final Enumerable<TInner> inner,
      final Predicate2<TSource, TInner> predicate,
      Function2<TSource, TInner, TResult> resultSelector,
      final boolean generateNullsOnLeft,
      final boolean generateNullsOnRight); static Enumerable<TSource> where(
      final Enumerable<TSource> source, final Predicate1<TSource> predicate); static Enumerable<Row> toRow(final Enumerable<Object[]> enumerator); static Enumerable<TResult> mergeJoin(final Enumerable<TSource> outer,
      final Enumerable<TInner> inner,
      final Function1<TSource, TKey> outerKeySelector,
      final Function1<TInner, TKey> innerKeySelector,
      final Function2<TSource, TInner, TResult> resultSelector,
      boolean generateNullsOnLeft,
      boolean generateNullsOnRight);  }
@Test public void testThetaFullJoinBothEmpty() { assertThat( Enumerables.thetaJoin(EMPS.take(0), DEPTS.take(0), EQUAL_DEPTNO, EMP_DEPT_TO_STRING, true, true).toList().toString(), equalTo("[]")); }
@Test public void testThetaJoin() { assertThat( Enumerables.thetaJoin(EMPS, DEPTS, EQUAL_DEPTNO, EMP_DEPT_TO_STRING, false, false).toList().toString(), equalTo("[{Theodore, 20, 20, Sales}, {Sebastian, 20, 20, Sales}]")); }
@Test public void testThetaLeftJoin() { assertThat( Enumerables.thetaJoin(EMPS, DEPTS, EQUAL_DEPTNO, EMP_DEPT_TO_STRING, false, true).toList().toString(), equalTo("[{Fred, 10, null, null}, {Theodore, 20, 20, Sales}, " + "{Sebastian, 20, 20, Sales}, {Joe, 30, null, null}]")); }
@Test public void testThetaRightJoin() { assertThat( Enumerables.thetaJoin(EMPS, DEPTS, EQUAL_DEPTNO, EMP_DEPT_TO_STRING, true, false).toList().toString(), equalTo("[{Theodore, 20, 20, Sales}, {Sebastian, 20, 20, Sales}, " + "{null, null, 15, Marketing}]")); }
@Test public void testThetaFullJoin() { assertThat( Enumerables.thetaJoin(EMPS, DEPTS, EQUAL_DEPTNO, EMP_DEPT_TO_STRING, true, true).toList().toString(), equalTo("[{Fred, 10, null, null}, {Theodore, 20, 20, Sales}, " + "{Sebastian, 20, 20, Sales}, {Joe, 30, null, null}, " + "{null, null, 15, Marketing}]")); }
@Test public void testThetaFullJoinLeftEmpty() { assertThat( Enumerables.thetaJoin(EMPS.take(0), DEPTS, EQUAL_DEPTNO, EMP_DEPT_TO_STRING, true, true) .orderBy(Functions.<String>identitySelector()).toList().toString(), equalTo("[{null, null, 15, Marketing}, {null, null, 20, Sales}]")); }
@Test public void testThetaFullJoinRightEmpty() { assertThat( Enumerables.thetaJoin(EMPS, DEPTS.take(0), EQUAL_DEPTNO, EMP_DEPT_TO_STRING, true, true).toList().toString(), equalTo("[{Fred, 10, null, null}, {Theodore, 20, null, null}, " + "{Sebastian, 20, null, null}, {Joe, 30, null, null}]")); }