All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.calcite.linq4j.EnumerableDefaults Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to you under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.calcite.linq4j;

import org.apache.calcite.linq4j.function.BigDecimalFunction1;
import org.apache.calcite.linq4j.function.DoubleFunction1;
import org.apache.calcite.linq4j.function.EqualityComparer;
import org.apache.calcite.linq4j.function.FloatFunction1;
import org.apache.calcite.linq4j.function.Function0;
import org.apache.calcite.linq4j.function.Function1;
import org.apache.calcite.linq4j.function.Function2;
import org.apache.calcite.linq4j.function.Functions;
import org.apache.calcite.linq4j.function.IntegerFunction1;
import org.apache.calcite.linq4j.function.LongFunction1;
import org.apache.calcite.linq4j.function.NullableBigDecimalFunction1;
import org.apache.calcite.linq4j.function.NullableDoubleFunction1;
import org.apache.calcite.linq4j.function.NullableFloatFunction1;
import org.apache.calcite.linq4j.function.NullableIntegerFunction1;
import org.apache.calcite.linq4j.function.NullableLongFunction1;
import org.apache.calcite.linq4j.function.Predicate1;
import org.apache.calcite.linq4j.function.Predicate2;

import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;

import java.math.BigDecimal;
import java.util.AbstractList;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.RandomAccess;
import java.util.Set;
import java.util.TreeMap;

import static org.apache.calcite.linq4j.Linq4j.CollectionEnumerable;
import static org.apache.calcite.linq4j.Linq4j.ListEnumerable;
import static org.apache.calcite.linq4j.function.Functions.adapt;

/**
 * Default implementations of methods in the {@link Enumerable} interface.
 */
public abstract class EnumerableDefaults {

  /**
   * Applies an accumulator function over a sequence.
   */
  public static  TSource aggregate(Enumerable source,
      Function2 func) {
    TSource result = null;
    try (Enumerator os = source.enumerator()) {
      while (os.moveNext()) {
        TSource o = os.current();
        result = func.apply(result, o);
      }
      return result;
    }
  }

  /**
   * Applies an accumulator function over a
   * sequence. The specified seed value is used as the initial
   * accumulator value.
   */
  public static  TAccumulate aggregate(
      Enumerable source, TAccumulate seed,
      Function2 func) {
    TAccumulate result = seed;
    try (Enumerator os = source.enumerator()) {
      while (os.moveNext()) {
        TSource o = os.current();
        result = func.apply(result, o);
      }
      return result;
    }
  }

  /**
   * Applies an accumulator function over a
   * sequence. The specified seed value is used as the initial
   * accumulator value, and the specified function is used to select
   * the result value.
   */
  public static  TResult aggregate(
      Enumerable source, TAccumulate seed,
      Function2 func,
      Function1 selector) {
    TAccumulate accumulate = seed;
    try (Enumerator os = source.enumerator()) {
      while (os.moveNext()) {
        TSource o = os.current();
        accumulate = func.apply(accumulate, o);
      }
      return selector.apply(accumulate);
    }
  }

  /**
   * Determines whether all elements of a sequence
   * satisfy a condition.
   */
  public static  boolean all(Enumerable enumerable,
      Predicate1 predicate) {
    try (Enumerator os = enumerable.enumerator()) {
      while (os.moveNext()) {
        TSource o = os.current();
        if (!predicate.apply(o)) {
          return false;
        }
      }
      return true;
    }
  }

  /**
   * Determines whether a sequence contains any
   * elements.
   */
  public static boolean any(Enumerable enumerable) {
    return enumerable.enumerator().moveNext();
  }

  /**
   * Determines whether any element of a sequence
   * satisfies a condition.
   */
  public static  boolean any(Enumerable enumerable,
      Predicate1 predicate) {
    try (Enumerator os = enumerable.enumerator()) {
      while (os.moveNext()) {
        TSource o = os.current();
        if (predicate.apply(o)) {
          return true;
        }
      }
      return false;
    }
  }

  /**
   * Returns the input typed as {@code Enumerable}.
   *
   * 

This method has no effect other than to change the compile-time type of * source from a type that implements {@code Enumerable} to * {@code Enumerable} itself. * *

{@code AsEnumerable(Enumerable)} can be used to choose * between query implementations when a sequence implements * {@code Enumerable} but also has a different set of public query * methods available. For example, given a generic class {@code Table} that * implements {@code Enumerable} and has its own methods such as * {@code where}, {@code select}, and {@code selectMany}, a call to * {@code where} would invoke the public {@code where} method of * {@code Table}. A {@code Table} type that represents a database table could * have a {@code where} method that takes the predicate argument as an * expression tree and converts the tree to SQL for remote execution. If * remote execution is not desired, for example because the predicate invokes * a local method, the {@code asEnumerable} method can be used to * hide the custom methods and instead make the standard query operators * available. */ public static Enumerable asEnumerable( Enumerable enumerable) { return enumerable; } /** * Converts an Enumerable to an IQueryable. * *

Analogous to the LINQ's Enumerable.AsQueryable extension method.

* * @param enumerable Enumerable * @param Element type * * @return A queryable */ public static Queryable asQueryable( Enumerable enumerable) { throw Extensions.todo(); } /** * Computes the average of a sequence of Decimal * values that are obtained by invoking a transform function on * each element of the input sequence. */ public static BigDecimal average(Enumerable source, BigDecimalFunction1 selector) { return sum(source, selector).divide(BigDecimal.valueOf(longCount(source))); } /** * Computes the average of a sequence of nullable * Decimal values that are obtained by invoking a transform * function on each element of the input sequence. */ public static BigDecimal average(Enumerable source, NullableBigDecimalFunction1 selector) { return sum(source, selector).divide(BigDecimal.valueOf(longCount(source))); } /** * Computes the average of a sequence of Double * values that are obtained by invoking a transform function on * each element of the input sequence. */ public static double average(Enumerable source, DoubleFunction1 selector) { return sum(source, selector) / longCount(source); } /** * Computes the average of a sequence of nullable * Double values that are obtained by invoking a transform * function on each element of the input sequence. */ public static Double average(Enumerable source, NullableDoubleFunction1 selector) { return sum(source, selector) / longCount(source); } /** * Computes the average of a sequence of int values * that are obtained by invoking a transform function on each * element of the input sequence. */ public static int average(Enumerable source, IntegerFunction1 selector) { return sum(source, selector) / count(source); } /** * Computes the average of a sequence of nullable * int values that are obtained by invoking a transform function * on each element of the input sequence. */ public static Integer average(Enumerable source, NullableIntegerFunction1 selector) { return sum(source, selector) / count(source); } /** * Computes the average of a sequence of long values * that are obtained by invoking a transform function on each * element of the input sequence. */ public static long average(Enumerable source, LongFunction1 selector) { return sum(source, selector) / longCount(source); } /** * Computes the average of a sequence of nullable * long values that are obtained by invoking a transform function * on each element of the input sequence. */ public static Long average(Enumerable source, NullableLongFunction1 selector) { return sum(source, selector) / longCount(source); } /** * Computes the average of a sequence of Float * values that are obtained by invoking a transform function on * each element of the input sequence. */ public static float average(Enumerable source, FloatFunction1 selector) { return sum(source, selector) / longCount(source); } /** * Computes the average of a sequence of nullable * Float values that are obtained by invoking a transform * function on each element of the input sequence. */ public static Float average(Enumerable source, NullableFloatFunction1 selector) { return sum(source, selector) / longCount(source); } /** *

Analogous to LINQ's Enumerable.Cast extension method.

* * @param clazz Target type * @param Target type * * @return Collection of T2 */ public static Enumerable cast( final Enumerable source, final Class clazz) { return new AbstractEnumerable() { public Enumerator enumerator() { return new CastingEnumerator<>(source.enumerator(), clazz); } }; } /** * Concatenates two sequences. */ public static Enumerable concat( Enumerable enumerable0, Enumerable enumerable1) { //noinspection unchecked return Linq4j.concat( Arrays.asList(enumerable0, enumerable1)); } /** * Determines whether a sequence contains a specified * element by using the default equality comparer. */ public static boolean contains(Enumerable enumerable, TSource element) { // Implementations of Enumerable backed by a Collection call // Collection.contains, which may be more efficient, not this method. try (Enumerator os = enumerable.enumerator()) { while (os.moveNext()) { TSource o = os.current(); if (o.equals(element)) { return true; } } return false; } } /** * Determines whether a sequence contains a specified * element by using a specified {@code EqualityComparer}. */ public static boolean contains(Enumerable enumerable, TSource element, EqualityComparer comparer) { for (TSource o : enumerable) { if (comparer.equal(o, element)) { return true; } } return false; } /** * Returns the number of elements in a * sequence. */ public static int count(Enumerable enumerable) { return (int) longCount(enumerable, Functions.truePredicate1()); } /** * Returns a number that represents how many elements * in the specified sequence satisfy a condition. */ public static int count(Enumerable enumerable, Predicate1 predicate) { return (int) longCount(enumerable, predicate); } /** * Returns the elements of the specified sequence or * the type parameter's default value in a singleton collection if * the sequence is empty. */ public static Enumerable defaultIfEmpty( Enumerable enumerable) { return defaultIfEmpty(enumerable, null); } /** * Returns the elements of the specified sequence or * the specified value in a singleton collection if the sequence * is empty. */ public static Enumerable defaultIfEmpty( Enumerable enumerable, TSource value) { try (Enumerator os = enumerable.enumerator()) { if (os.moveNext()) { return Linq4j.asEnumerable(() -> new Iterator() { private boolean nonFirst; private Iterator rest; public boolean hasNext() { return !nonFirst || rest.hasNext(); } public TSource next() { if (nonFirst) { return rest.next(); } else { final TSource first = os.current(); nonFirst = true; rest = Linq4j.enumeratorIterator(os); return first; } } public void remove() { throw new UnsupportedOperationException("remove"); } }); } else { return Linq4j.singletonEnumerable(value); } } } /** * Returns distinct elements from a sequence by using * the default {@link EqualityComparer} to compare values. */ public static Enumerable distinct( Enumerable enumerable) { final Enumerator os = enumerable.enumerator(); final Set set = new HashSet<>(); while (os.moveNext()) { set.add(os.current()); } os.close(); return Linq4j.asEnumerable(set); } /** * Returns distinct elements from a sequence by using * a specified {@link EqualityComparer} to compare values. */ public static Enumerable distinct( Enumerable enumerable, EqualityComparer comparer) { if (comparer == Functions.identityComparer()) { return distinct(enumerable); } final Set> set = new HashSet<>(); Function1> wrapper = wrapperFor(comparer); Function1, TSource> unwrapper = unwrapper(); enumerable.select(wrapper).into(set); return Linq4j.asEnumerable(set).select(unwrapper); } /** * Returns the element at a specified index in a * sequence. */ public static TSource elementAt(Enumerable enumerable, int index) { final ListEnumerable list = enumerable instanceof ListEnumerable ? ((ListEnumerable) enumerable) : null; if (list != null) { return list.toList().get(index); } if (index < 0) { throw new IndexOutOfBoundsException(); } try (Enumerator os = enumerable.enumerator()) { while (true) { if (!os.moveNext()) { throw new IndexOutOfBoundsException(); } if (index == 0) { return os.current(); } index--; } } } /** * Returns the element at a specified index in a * sequence or a default value if the index is out of * range. */ public static TSource elementAtOrDefault( Enumerable enumerable, int index) { final ListEnumerable list = enumerable instanceof ListEnumerable ? ((ListEnumerable) enumerable) : null; if (index >= 0) { if (list != null) { final List rawList = list.toList(); if (index < rawList.size()) { return rawList.get(index); } } else { try (Enumerator os = enumerable.enumerator()) { while (true) { if (!os.moveNext()) { break; } if (index == 0) { return os.current(); } index--; } } } } return null; } /** * Produces the set difference of two sequences by * using the default equality comparer to compare values. (Defined * by Enumerable.) */ public static Enumerable except( Enumerable source0, Enumerable source1) { Set set = new HashSet<>(); source0.into(set); try (Enumerator os = source1.enumerator()) { while (os.moveNext()) { TSource o = os.current(); set.remove(o); } return Linq4j.asEnumerable(set); } } /** * Produces the set difference of two sequences by * using the specified {@code EqualityComparer} to compare * values. */ public static Enumerable except( Enumerable source0, Enumerable source1, EqualityComparer comparer) { if (comparer == Functions.identityComparer()) { return except(source0, source1); } Set> set = new HashSet<>(); Function1> wrapper = wrapperFor(comparer); source0.select(wrapper).into(set); try (Enumerator> os = source1.select(wrapper).enumerator()) { while (os.moveNext()) { Wrapped o = os.current(); set.remove(o); } } Function1, TSource> unwrapper = unwrapper(); return Linq4j.asEnumerable(set).select(unwrapper); } /** * Returns the first element of a sequence. (Defined * by Enumerable.) */ public static TSource first(Enumerable enumerable) { try (Enumerator os = enumerable.enumerator()) { if (os.moveNext()) { return os.current(); } throw new NoSuchElementException(); } } /** * Returns the first element in a sequence that * satisfies a specified condition. */ public static TSource first(Enumerable enumerable, Predicate1 predicate) { for (TSource o : enumerable) { if (predicate.apply(o)) { return o; } } throw new NoSuchElementException(); } /** * Returns the first element of a sequence, or a * default value if the sequence contains no elements. */ public static TSource firstOrDefault( Enumerable enumerable) { try (Enumerator os = enumerable.enumerator()) { if (os.moveNext()) { return os.current(); } return null; } } /** * Returns the first element of the sequence that * satisfies a condition or a default value if no such element is * found. */ public static TSource firstOrDefault(Enumerable enumerable, Predicate1 predicate) { for (TSource o : enumerable) { if (predicate.apply(o)) { return o; } } return null; } /** * Groups the elements of a sequence according to a * specified key selector function. */ public static Enumerable> groupBy( final Enumerable enumerable, final Function1 keySelector) { return enumerable.toLookup(keySelector); } /** * Groups the elements of a sequence according to a * specified key selector function and compares the keys by using * a specified comparer. */ public static Enumerable> groupBy( Enumerable enumerable, Function1 keySelector, EqualityComparer comparer) { return enumerable.toLookup(keySelector, comparer); } /** * Groups the elements of a sequence according to a * specified key selector function and projects the elements for * each group by using a specified function. */ public static Enumerable> groupBy( Enumerable enumerable, Function1 keySelector, Function1 elementSelector) { return enumerable.toLookup(keySelector, elementSelector); } /** * Groups the elements of a sequence according to a * key selector function. The keys are compared by using a * comparer and each group's elements are projected by using a * specified function. */ public static Enumerable> groupBy( Enumerable enumerable, Function1 keySelector, Function1 elementSelector, EqualityComparer comparer) { return enumerable.toLookup(keySelector, elementSelector, comparer); } /** * Groups the elements of a sequence according to a * specified key selector function and creates a result value from * each group and its key. */ public static Enumerable groupBy( Enumerable enumerable, Function1 keySelector, final Function2, TResult> resultSelector) { return enumerable.toLookup(keySelector) .select(group -> resultSelector.apply(group.getKey(), group)); } /** * Groups the elements of a sequence according to a * specified key selector function and creates a result value from * each group and its key. The keys are compared by using a * specified comparer. */ public static Enumerable groupBy( Enumerable enumerable, Function1 keySelector, final Function2, TResult> resultSelector, EqualityComparer comparer) { return enumerable.toLookup(keySelector, comparer) .select(group -> resultSelector.apply(group.getKey(), group)); } /** * Groups the elements of a sequence according to a * specified key selector function and creates a result value from * each group and its key. The elements of each group are * projected by using a specified function. */ public static Enumerable groupBy( Enumerable enumerable, Function1 keySelector, Function1 elementSelector, final Function2, TResult> resultSelector) { return enumerable.toLookup(keySelector, elementSelector) .select(group -> resultSelector.apply(group.getKey(), group)); } /** * Groups the elements of a sequence according to a * specified key selector function and creates a result value from * each group and its key. Key values are compared by using a * specified comparer, and the elements of each group are * projected by using a specified function. */ public static Enumerable groupBy( Enumerable enumerable, Function1 keySelector, Function1 elementSelector, final Function2, TResult> resultSelector, EqualityComparer comparer) { return enumerable.toLookup(keySelector, elementSelector, comparer) .select(group -> resultSelector.apply(group.getKey(), group)); } /** * Groups the elements of a sequence according to a * specified key selector function, initializing an accumulator for each * group and adding to it each time an element with the same key is seen. * Creates a result value from each accumulator and its key using a * specified function. */ public static Enumerable groupBy( Enumerable enumerable, Function1 keySelector, Function0 accumulatorInitializer, Function2 accumulatorAdder, final Function2 resultSelector) { return groupBy_(new HashMap<>(), enumerable, keySelector, accumulatorInitializer, accumulatorAdder, resultSelector); } /** * Groups the elements of a sequence according to a list of * specified key selector functions, initializing an accumulator for each * group and adding to it each time an element with the same key is seen. * Creates a result value from each accumulator and its key using a * specified function. * *

This method exists to support SQL {@code GROUPING SETS}. * It does not correspond to any method in {@link Enumerable}. */ public static Enumerable groupByMultiple( Enumerable enumerable, List> keySelectors, Function0 accumulatorInitializer, Function2 accumulatorAdder, final Function2 resultSelector) { return groupByMultiple_( new HashMap<>(), enumerable, keySelectors, accumulatorInitializer, accumulatorAdder, resultSelector); } /** * Groups the elements of a sequence according to a * specified key selector function, initializing an accumulator for each * group and adding to it each time an element with the same key is seen. * Creates a result value from each accumulator and its key using a * specified function. Key values are compared by using a * specified comparer. */ public static Enumerable groupBy( Enumerable enumerable, Function1 keySelector, Function0 accumulatorInitializer, Function2 accumulatorAdder, Function2 resultSelector, EqualityComparer comparer) { return groupBy_( new WrapMap<>( // Java 8 cannot infer return type with HashMap::new is used () -> new HashMap, TAccumulate>(), comparer), enumerable, keySelector, accumulatorInitializer, accumulatorAdder, resultSelector); } private static Enumerable groupBy_( final Map map, Enumerable enumerable, Function1 keySelector, Function0 accumulatorInitializer, Function2 accumulatorAdder, final Function2 resultSelector) { try (Enumerator os = enumerable.enumerator()) { while (os.moveNext()) { TSource o = os.current(); TKey key = keySelector.apply(o); TAccumulate accumulator = map.get(key); if (accumulator == null) { accumulator = accumulatorInitializer.apply(); accumulator = accumulatorAdder.apply(accumulator, o); map.put(key, accumulator); } else { TAccumulate accumulator0 = accumulator; accumulator = accumulatorAdder.apply(accumulator, o); if (accumulator != accumulator0) { map.put(key, accumulator); } } } } return new LookupResultEnumerable<>(map, resultSelector); } private static Enumerable groupByMultiple_( final Map map, Enumerable enumerable, List> keySelectors, Function0 accumulatorInitializer, Function2 accumulatorAdder, final Function2 resultSelector) { try (Enumerator os = enumerable.enumerator()) { while (os.moveNext()) { for (Function1 keySelector : keySelectors) { TSource o = os.current(); TKey key = keySelector.apply(o); TAccumulate accumulator = map.get(key); if (accumulator == null) { accumulator = accumulatorInitializer.apply(); accumulator = accumulatorAdder.apply(accumulator, o); map.put(key, accumulator); } else { TAccumulate accumulator0 = accumulator; accumulator = accumulatorAdder.apply(accumulator, o); if (accumulator != accumulator0) { map.put(key, accumulator); } } } } } return new LookupResultEnumerable<>(map, resultSelector); } private static Enumerable groupBy_( final Set map, Enumerable enumerable, Function1 keySelector, final Function1 resultSelector) { try (Enumerator os = enumerable.enumerator()) { while (os.moveNext()) { TSource o = os.current(); TKey key = keySelector.apply(o); map.add(key); } } return Linq4j.asEnumerable(map).select(resultSelector); } /** * Correlates the elements of two sequences based on * equality of keys and groups the results. The default equality * comparer is used to compare keys. */ public static Enumerable groupJoin( final Enumerable outer, final Enumerable inner, final Function1 outerKeySelector, final Function1 innerKeySelector, final Function2, TResult> resultSelector) { return new AbstractEnumerable() { final Map outerMap = outer.toMap(outerKeySelector); final Lookup innerLookup = inner.toLookup(innerKeySelector); final Enumerator> entries = Linq4j.enumerator(outerMap.entrySet()); public Enumerator enumerator() { return new Enumerator() { public TResult current() { final Map.Entry entry = entries.current(); final Enumerable inners = innerLookup.get(entry.getKey()); return resultSelector.apply(entry.getValue(), inners == null ? Linq4j.emptyEnumerable() : inners); } public boolean moveNext() { return entries.moveNext(); } public void reset() { entries.reset(); } public void close() { } }; } }; } /** * Correlates the elements of two sequences based on * key equality and groups the results. A specified * {@code EqualityComparer} is used to compare keys. */ public static Enumerable groupJoin( final Enumerable outer, final Enumerable inner, final Function1 outerKeySelector, final Function1 innerKeySelector, final Function2, TResult> resultSelector, final EqualityComparer comparer) { return new AbstractEnumerable() { final Map outerMap = outer.toMap(outerKeySelector, comparer); final Lookup innerLookup = inner.toLookup(innerKeySelector, comparer); final Enumerator> entries = Linq4j.enumerator(outerMap.entrySet()); public Enumerator enumerator() { return new Enumerator() { public TResult current() { final Map.Entry entry = entries.current(); final Enumerable inners = innerLookup.get(entry.getKey()); return resultSelector.apply(entry.getValue(), inners == null ? Linq4j.emptyEnumerable() : inners); } public boolean moveNext() { return entries.moveNext(); } public void reset() { entries.reset(); } public void close() { } }; } }; } /** * Produces the set intersection of two sequences by * using the default equality comparer to compare values. (Defined * by Enumerable.) */ public static Enumerable intersect( Enumerable source0, Enumerable source1) { Set set0 = new HashSet<>(); source0.into(set0); Set set1 = new HashSet<>(); try (Enumerator os = source1.enumerator()) { while (os.moveNext()) { TSource o = os.current(); if (set0.contains(o)) { set1.add(o); } } } return Linq4j.asEnumerable(set1); } /** * Produces the set intersection of two sequences by * using the specified {@code EqualityComparer} to compare * values. */ public static Enumerable intersect( Enumerable source0, Enumerable source1, EqualityComparer comparer) { if (comparer == Functions.identityComparer()) { return intersect(source0, source1); } Set> set0 = new HashSet<>(); Function1> wrapper = wrapperFor(comparer); source0.select(wrapper).into(set0); Set> set1 = new HashSet<>(); try (Enumerator> os = source1.select(wrapper).enumerator()) { while (os.moveNext()) { Wrapped o = os.current(); if (set0.contains(o)) { set1.add(o); } } } Function1, TSource> unwrapper = unwrapper(); return Linq4j.asEnumerable(set1).select(unwrapper); } /** * Correlates the elements of two sequences based on * matching keys. The default equality comparer is used to compare * keys. */ public static Enumerable hashJoin( final Enumerable outer, final Enumerable inner, final Function1 outerKeySelector, final Function1 innerKeySelector, final Function2 resultSelector) { return hashJoin( outer, inner, outerKeySelector, innerKeySelector, resultSelector, null, false, false); } /** * Correlates the elements of two sequences based on * matching keys. A specified {@code EqualityComparer} is used to * compare keys. */ public static Enumerable hashJoin( Enumerable outer, Enumerable inner, Function1 outerKeySelector, Function1 innerKeySelector, Function2 resultSelector, EqualityComparer comparer) { return hashJoin( outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer, false, false); } /** * Correlates the elements of two sequences based on * matching keys. A specified {@code EqualityComparer} is used to * compare keys. */ public static Enumerable hashJoin( Enumerable outer, Enumerable inner, Function1 outerKeySelector, Function1 innerKeySelector, Function2 resultSelector, EqualityComparer comparer, boolean generateNullsOnLeft, boolean generateNullsOnRight) { return hashEquiJoin_( outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer, generateNullsOnLeft, generateNullsOnRight); } /** * Correlates the elements of two sequences based on * matching keys. A specified {@code EqualityComparer} is used to * compare keys.A predicate is used to filter the join result per-row. */ public static Enumerable hashJoin( Enumerable outer, Enumerable inner, Function1 outerKeySelector, Function1 innerKeySelector, Function2 resultSelector, EqualityComparer comparer, boolean generateNullsOnLeft, boolean generateNullsOnRight, Predicate2 predicate) { if (predicate == null) { return hashEquiJoin_( outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer, generateNullsOnLeft, generateNullsOnRight); } else { return hashJoinWithPredicate_( outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer, generateNullsOnLeft, generateNullsOnRight, predicate); } } /** Implementation of join that builds the right input and probes with the * left. */ private static Enumerable hashEquiJoin_( final Enumerable outer, final Enumerable inner, final Function1 outerKeySelector, final Function1 innerKeySelector, final Function2 resultSelector, final EqualityComparer comparer, final boolean generateNullsOnLeft, final boolean generateNullsOnRight) { return new AbstractEnumerable() { public Enumerator enumerator() { final Lookup innerLookup = comparer == null ? inner.toLookup(innerKeySelector) : inner.toLookup(innerKeySelector, comparer); return new Enumerator() { Enumerator outers = outer.enumerator(); Enumerator inners = Linq4j.emptyEnumerator(); Set unmatchedKeys = generateNullsOnLeft ? new HashSet<>(innerLookup.keySet()) : null; public TResult current() { return resultSelector.apply(outers.current(), inners.current()); } public boolean moveNext() { for (;;) { if (inners.moveNext()) { return true; } if (!outers.moveNext()) { if (unmatchedKeys != null) { // We've seen everything else. If we are doing a RIGHT or FULL // join (leftNull = true) there are any keys which right but // not the left. List list = new ArrayList<>(); for (TKey key : unmatchedKeys) { for (TInner tInner : innerLookup.get(key)) { list.add(tInner); } } inners = Linq4j.enumerator(list); outers.close(); outers = Linq4j.singletonNullEnumerator(); outers.moveNext(); unmatchedKeys = null; // don't do the 'leftovers' again continue; } return false; } final TSource outer = outers.current(); final Enumerable innerEnumerable; if (outer == null) { innerEnumerable = null; } else { final TKey outerKey = outerKeySelector.apply(outer); if (outerKey == null) { innerEnumerable = null; } else { if (unmatchedKeys != null) { unmatchedKeys.remove(outerKey); } innerEnumerable = innerLookup.get(outerKey); } } if (innerEnumerable == null || !innerEnumerable.any()) { if (generateNullsOnRight) { inners = Linq4j.singletonNullEnumerator(); } else { inners = Linq4j.emptyEnumerator(); } } else { inners = innerEnumerable.enumerator(); } } } public void reset() { outers.reset(); } public void close() { outers.close(); } }; } }; } /** Implementation of join that builds the right input and probes with the * left */ private static Enumerable hashJoinWithPredicate_( final Enumerable outer, final Enumerable inner, final Function1 outerKeySelector, final Function1 innerKeySelector, final Function2 resultSelector, final EqualityComparer comparer, final boolean generateNullsOnLeft, final boolean generateNullsOnRight, final Predicate2 predicate) { return new AbstractEnumerable() { public Enumerator enumerator() { /** * the innerToLookUp will refer the inner , if current join * is a right join, we should figure out the right list first, if * not, then keep the original inner here. */ final Enumerable innerToLookUp = generateNullsOnLeft ? Linq4j.asEnumerable(inner.toList()) : inner; final Lookup innerLookup = comparer == null ? innerToLookUp.toLookup(innerKeySelector) : innerToLookUp .toLookup(innerKeySelector, comparer); return new Enumerator() { Enumerator outers = outer.enumerator(); Enumerator inners = Linq4j.emptyEnumerator(); List innersUnmatched = generateNullsOnLeft ? new ArrayList<>(innerToLookUp.toList()) : null; public TResult current() { return resultSelector.apply(outers.current(), inners.current()); } public boolean moveNext() { for (;;) { if (inners.moveNext()) { return true; } if (!outers.moveNext()) { if (innersUnmatched != null) { inners = Linq4j.enumerator(innersUnmatched); outers.close(); outers = Linq4j.singletonNullEnumerator(); outers.moveNext(); innersUnmatched = null; // don't do the 'leftovers' again continue; } return false; } final TSource outer = outers.current(); Enumerable innerEnumerable; if (outer == null) { innerEnumerable = null; } else { final TKey outerKey = outerKeySelector.apply(outer); if (outerKey == null) { innerEnumerable = null; } else { innerEnumerable = innerLookup.get(outerKey); //apply predicate to filter per-row if (innerEnumerable != null) { final List matchedInners = new ArrayList<>(); try (Enumerator innerEnumerator = innerEnumerable.enumerator()) { while (innerEnumerator.moveNext()) { final TInner inner = innerEnumerator.current(); if (predicate.apply(outer, inner)) { matchedInners.add(inner); } } } innerEnumerable = Linq4j.asEnumerable(matchedInners); if (innersUnmatched != null) { innersUnmatched.removeAll(matchedInners); } } } } if (innerEnumerable == null || !innerEnumerable.any()) { if (generateNullsOnRight) { inners = Linq4j.singletonNullEnumerator(); } else { inners = Linq4j.emptyEnumerator(); } } else { inners = innerEnumerable.enumerator(); } } } public void reset() { outers.reset(); } public void close() { outers.close(); } }; } }; } /** * For each row of the {@code outer} enumerable returns the correlated rows * from the {@code inner} enumerable. */ public static Enumerable correlateJoin( final JoinType joinType, final Enumerable outer, final Function1> inner, final Function2 resultSelector) { if (joinType == JoinType.RIGHT || joinType == JoinType.FULL) { throw new IllegalArgumentException("JoinType " + joinType + " is not valid for correlation"); } return new AbstractEnumerable() { public Enumerator enumerator() { return new Enumerator() { private Enumerator outerEnumerator = outer.enumerator(); private Enumerator innerEnumerator; TSource outerValue; TInner innerValue; int state = 0; // 0 -- moving outer, 1 moving inner; public TResult current() { return resultSelector.apply(outerValue, innerValue); } public boolean moveNext() { while (true) { switch (state) { case 0: // move outer if (!outerEnumerator.moveNext()) { return false; } outerValue = outerEnumerator.current(); // initial move inner Enumerable innerEnumerable = inner.apply(outerValue); if (innerEnumerable == null) { innerEnumerable = Linq4j.emptyEnumerable(); } if (innerEnumerator != null) { innerEnumerator.close(); } innerEnumerator = innerEnumerable.enumerator(); if (innerEnumerator.moveNext()) { switch (joinType) { case ANTI: // For anti-join need to try next outer row // Current does not match continue; case SEMI: return true; // current row matches } // INNER and LEFT just return result innerValue = innerEnumerator.current(); state = 1; // iterate over inner results return true; } // No match detected innerValue = null; switch (joinType) { case LEFT: case ANTI: return true; } // For INNER and LEFT need to find another outer row continue; case 1: // subsequent move inner if (innerEnumerator.moveNext()) { innerValue = innerEnumerator.current(); return true; } state = 0; // continue loop, move outer } } } public void reset() { state = 0; outerEnumerator.reset(); closeInner(); } public void close() { outerEnumerator.close(); closeInner(); outerValue = null; } private void closeInner() { innerValue = null; if (innerEnumerator != null) { innerEnumerator.close(); innerEnumerator = null; } } }; } }; } /** * Returns the last element of a sequence. (Defined * by Enumerable.) */ public static TSource last(Enumerable enumerable) { final ListEnumerable list = enumerable instanceof ListEnumerable ? ((ListEnumerable) enumerable) : null; if (list != null) { final List rawList = list.toList(); final int count = rawList.size(); if (count > 0) { return rawList.get(count - 1); } } else { try (Enumerator os = enumerable.enumerator()) { if (os.moveNext()) { TSource result; do { result = os.current(); } while (os.moveNext()); return result; } } } throw new NoSuchElementException(); } /** *

Fetches blocks of size {@code batchSize} from {@code outer}, * storing each block into a list ({@code outerValues}). * For each block, it uses the {@code inner} function to * obtain an enumerable with the correlated rows from the right (inner) input.

* *

Each result present in the {@code innerEnumerator} has matched at least one * value from the block {@code outerValues}. * At this point a mini nested loop is performed between the outer values * and inner values using the {@code predicate} to find out the actual matching join results.

* *

In order to optimize this mini nested loop, during the first iteration * (the first value from {@code outerValues}) we use the {@code innerEnumerator} * to compare it to inner rows, and at the same time we fill a list ({@code innerValues}) * with said {@code innerEnumerator} rows. In the subsequent iterations * (2nd, 3rd, etc. value from {@code outerValues}) the list {@code innerValues} is used, * since it contains all the {@code innerEnumerator} values, * which were stored in the first iteration.

*/ public static Enumerable correlateBatchJoin( final JoinType joinType, final Enumerable outer, final Function1, Enumerable> inner, final Function2 resultSelector, final Predicate2 predicate, final int batchSize) { return new AbstractEnumerable() { @Override public Enumerator enumerator() { return new Enumerator() { Enumerator outerEnumerator = outer.enumerator(); List outerValues = new ArrayList<>(batchSize); List innerValues = new ArrayList<>(); TSource outerValue; TInner innerValue; Enumerable innerEnumerable; Enumerator innerEnumerator; boolean innerEnumHasNext = false; boolean atLeastOneResult = false; int i = -1; // outer position int j = -1; // inner position @Override public TResult current() { return resultSelector.apply(outerValue, innerValue); } @Override public boolean moveNext() { while (true) { // Fetch a new batch if (i == outerValues.size() || i == -1) { i = 0; j = 0; outerValues.clear(); innerValues.clear(); while (outerValues.size() < batchSize && outerEnumerator.moveNext()) { TSource tSource = outerEnumerator.current(); outerValues.add(tSource); } if (outerValues.isEmpty()) { return false; } innerEnumerable = inner.apply(new AbstractList() { // If the last batch isn't complete fill it with the first value // No harm since it's a disjunction @Override public TSource get(final int index) { return index < outerValues.size() ? outerValues.get(index) : outerValues.get(0); } @Override public int size() { return batchSize; } }); if (innerEnumerable == null) { innerEnumerable = Linq4j.emptyEnumerable(); } innerEnumerator = innerEnumerable.enumerator(); innerEnumHasNext = innerEnumerator.moveNext(); // If no inner values skip the whole batch // in case of SEMI and INNER join if (!innerEnumHasNext && (joinType == JoinType.SEMI || joinType == JoinType.INNER)) { i = outerValues.size(); continue; } } if (innerHasNext()) { outerValue = outerValues.get(i); // get current outer value nextInnerValue(); // Compare current block row to current inner value if (predicate.apply(outerValue, innerValue)) { atLeastOneResult = true; // Skip the rest of inner values in case of // ANTI and SEMI when a match is found if (joinType == JoinType.ANTI || joinType == JoinType.SEMI) { // Two ways of skipping inner values, // enumerator way and ArrayList way if (i == 0) { while (innerEnumHasNext) { innerValues.add(innerEnumerator.current()); innerEnumHasNext = innerEnumerator.moveNext(); } } else { j = innerValues.size(); } if (joinType == JoinType.ANTI) { continue; } } return true; } } else { // End of inner if (!atLeastOneResult && (joinType == JoinType.LEFT || joinType == JoinType.ANTI)) { outerValue = outerValues.get(i); // get current outer value innerValue = null; nextOuterValue(); return true; } nextOuterValue(); } } } public void nextOuterValue() { i++; // next outerValue j = 0; // rewind innerValues atLeastOneResult = false; } private void nextInnerValue() { if (i == 0) { innerValue = innerEnumerator.current(); innerValues.add(innerValue); innerEnumHasNext = innerEnumerator.moveNext(); // next enumerator inner value } else { innerValue = innerValues.get(j++); // next ArrayList inner value } } private boolean innerHasNext() { return i == 0 ? innerEnumHasNext : j < innerValues.size(); } @Override public void reset() { outerEnumerator.reset(); innerValue = null; outerValue = null; outerValues.clear(); innerValues.clear(); atLeastOneResult = false; i = -1; } @Override public void close() { outerEnumerator.close(); if (innerEnumerator != null) { innerEnumerator.close(); } outerValue = null; innerValue = null; } }; } }; } /** * Returns elements of {@code outer} for which there is a member of * {@code inner} with a matching key. */ public static Enumerable semiJoin( final Enumerable outer, final Enumerable inner, final Function1 outerKeySelector, final Function1 innerKeySelector) { return semiEquiJoin_(outer, inner, outerKeySelector, innerKeySelector, null, false); } public static Enumerable semiJoin( final Enumerable outer, final Enumerable inner, final Function1 outerKeySelector, final Function1 innerKeySelector, final EqualityComparer comparer) { return semiEquiJoin_(outer, inner, outerKeySelector, innerKeySelector, comparer, false); } public static Enumerable semiJoin( final Enumerable outer, final Enumerable inner, final Function1 outerKeySelector, final Function1 innerKeySelector, final EqualityComparer comparer, final Predicate2 nonEquiPredicate) { return semiJoin(outer, inner, outerKeySelector, innerKeySelector, comparer, false, nonEquiPredicate); } /** * Returns elements of {@code outer} for which there is NOT a member of * {@code inner} with a matching key. */ public static Enumerable antiJoin( final Enumerable outer, final Enumerable inner, final Function1 outerKeySelector, final Function1 innerKeySelector) { return semiEquiJoin_(outer, inner, outerKeySelector, innerKeySelector, null, true); } public static Enumerable antiJoin( final Enumerable outer, final Enumerable inner, final Function1 outerKeySelector, final Function1 innerKeySelector, final EqualityComparer comparer) { return semiEquiJoin_(outer, inner, outerKeySelector, innerKeySelector, comparer, true); } public static Enumerable antiJoin( final Enumerable outer, final Enumerable inner, final Function1 outerKeySelector, final Function1 innerKeySelector, final EqualityComparer comparer, final Predicate2 nonEquiPredicate) { return semiJoin(outer, inner, outerKeySelector, innerKeySelector, comparer, true, nonEquiPredicate); } /** * Returns elements of {@code outer} for which there is (semi-join) / is not (anti-semi-join) * a member of {@code inner} with a matching key. A specified * {@code EqualityComparer} is used to compare keys. * A predicate is used to filter the join result per-row. */ public static Enumerable semiJoin( final Enumerable outer, final Enumerable inner, final Function1 outerKeySelector, final Function1 innerKeySelector, final EqualityComparer comparer, final boolean anti, final Predicate2 nonEquiPredicate) { if (nonEquiPredicate == null) { return semiEquiJoin_(outer, inner, outerKeySelector, innerKeySelector, comparer, anti); } else { return semiJoinWithPredicate_(outer, inner, outerKeySelector, innerKeySelector, comparer, anti, nonEquiPredicate); } } private static Enumerable semiJoinWithPredicate_( final Enumerable outer, final Enumerable inner, final Function1 outerKeySelector, final Function1 innerKeySelector, final EqualityComparer comparer, final boolean anti, final Predicate2 nonEquiPredicate) { return new AbstractEnumerable() { public Enumerator enumerator() { // CALCITE-2909 Delay the computation of the innerLookup until the // moment when we are sure // that it will be really needed, i.e. when the first outer // enumerator item is processed final Supplier> innerLookup = Suppliers.memoize( () -> comparer == null ? inner.toLookup(innerKeySelector) : inner.toLookup(innerKeySelector, comparer)); final Predicate1 predicate = v0 -> { TKey key = outerKeySelector.apply(v0); if (!innerLookup.get().containsKey(key)) { return anti; } Enumerable innersOfKey = innerLookup.get().get(key); try (Enumerator os = innersOfKey.enumerator()) { while (os.moveNext()) { TInner v1 = os.current(); if (nonEquiPredicate.apply(v0, v1)) { return !anti; } } return anti; } }; return EnumerableDefaults.where(outer.enumerator(), predicate); } }; } /** * Returns elements of {@code outer} for which there is (semi-join) / is not (anti-semi-join) * a member of {@code inner} with a matching key. A specified * {@code EqualityComparer} is used to compare keys. */ private static Enumerable semiEquiJoin_( final Enumerable outer, final Enumerable inner, final Function1 outerKeySelector, final Function1 innerKeySelector, final EqualityComparer comparer, final boolean anti) { return new AbstractEnumerable() { public Enumerator enumerator() { // CALCITE-2909 Delay the computation of the innerLookup until the moment when we are sure // that it will be really needed, i.e. when the first outer enumerator item is processed final Supplier> innerLookup = Suppliers.memoize(() -> comparer == null ? inner.select(innerKeySelector).distinct() : inner.select(innerKeySelector).distinct(comparer)); final Predicate1 predicate = anti ? v0 -> !innerLookup.get().contains(outerKeySelector.apply(v0)) : v0 -> innerLookup.get().contains(outerKeySelector.apply(v0)); return EnumerableDefaults.where(outer.enumerator(), predicate); } }; } /** * Correlates the elements of two sequences based on a predicate. */ public static Enumerable nestedLoopJoin( final Enumerable outer, final Enumerable inner, final Predicate2 predicate, Function2 resultSelector, final JoinType joinType) { // Building the result as a list is easy but hogs memory. We should iterate. final boolean generateNullsOnLeft = joinType.generatesNullsOnLeft(); final boolean generateNullsOnRight = joinType.generatesNullsOnRight(); final List result = new ArrayList<>(); final Enumerator lefts = outer.enumerator(); final List rightList = inner.toList(); final Set 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 rights = Linq4j.iterableEnumerator(rightList); while (rights.moveNext()) { TInner right = rights.current(); if (predicate.apply(left, right)) { ++leftMatchCount; if (joinType == JoinType.ANTI) { break; } else { if (rightUnmatched != null) { rightUnmatched.remove(right); } result.add(resultSelector.apply(left, right)); } } } if (leftMatchCount == 0 && (generateNullsOnRight || joinType == JoinType.ANTI)) { result.add(resultSelector.apply(left, null)); } } if (rightUnmatched != null) { final Enumerator rights = Linq4j.iterableEnumerator(rightUnmatched); while (rights.moveNext()) { TInner right = rights.current(); result.add(resultSelector.apply(null, right)); } } return Linq4j.asEnumerable(result); } /** Joins two inputs that are sorted on the key. */ public static , TResult> Enumerable mergeJoin(final Enumerable outer, final Enumerable inner, final Function1 outerKeySelector, final Function1 innerKeySelector, final Function2 resultSelector, boolean generateNullsOnLeft, boolean generateNullsOnRight) { if (generateNullsOnLeft) { throw new UnsupportedOperationException( "not implemented, mergeJoin with generateNullsOnLeft"); } if (generateNullsOnRight) { throw new UnsupportedOperationException( "not implemented, mergeJoin with generateNullsOnRight"); } return new AbstractEnumerable() { public Enumerator enumerator() { return new MergeJoinEnumerator<>(outer.enumerator(), inner.enumerator(), outerKeySelector, innerKeySelector, resultSelector); } }; } /** * Returns the last element of a sequence that * satisfies a specified condition. */ public static TSource last(Enumerable enumerable, Predicate1 predicate) { final ListEnumerable list = enumerable instanceof ListEnumerable ? ((ListEnumerable) enumerable) : null; if (list != null) { final List rawList = list.toList(); final int count = rawList.size(); for (int i = count - 1; i >= 0; --i) { TSource result = rawList.get(i); if (predicate.apply(result)) { return result; } } } else { try (Enumerator os = enumerable.enumerator()) { while (os.moveNext()) { TSource result = os.current(); if (predicate.apply(result)) { while (os.moveNext()) { TSource element = os.current(); if (predicate.apply(element)) { result = element; } } return result; } } } } throw new NoSuchElementException(); } /** * Returns the last element of a sequence, or a * default value if the sequence contains no elements. */ public static TSource lastOrDefault( Enumerable enumerable) { final ListEnumerable list = enumerable instanceof ListEnumerable ? ((ListEnumerable) enumerable) : null; if (list != null) { final List rawList = list.toList(); final int count = rawList.size(); if (count > 0) { return rawList.get(count - 1); } } else { try (Enumerator os = enumerable.enumerator()) { if (os.moveNext()) { TSource result; do { result = os.current(); } while (os.moveNext()); return result; } } } return null; } /** * Returns the last element of a sequence that * satisfies a condition or a default value if no such element is * found. */ public static TSource lastOrDefault(Enumerable enumerable, Predicate1 predicate) { final ListEnumerable list = enumerable instanceof ListEnumerable ? ((ListEnumerable) enumerable) : null; if (list != null) { final List rawList = list.toList(); final int count = rawList.size(); for (int i = count - 1; i >= 0; --i) { TSource result = rawList.get(i); if (predicate.apply(result)) { return result; } } } else { try (Enumerator os = enumerable.enumerator()) { while (os.moveNext()) { TSource result = os.current(); if (predicate.apply(result)) { while (os.moveNext()) { TSource element = os.current(); if (predicate.apply(element)) { result = element; } } return result; } } } } return null; } /** * Returns an long that represents the total number * of elements in a sequence. */ public static long longCount(Enumerable source) { return longCount(source, Functions.truePredicate1()); } /** * Returns an long that represents how many elements * in a sequence satisfy a condition. */ public static long longCount(Enumerable enumerable, Predicate1 predicate) { // Shortcut if this is a collection and the predicate is always true. if (predicate == Predicate1.TRUE && enumerable instanceof Collection) { return ((Collection) enumerable).size(); } int n = 0; try (Enumerator os = enumerable.enumerator()) { while (os.moveNext()) { TSource o = os.current(); if (predicate.apply(o)) { ++n; } } } return n; } /** * Returns the maximum value in a generic * sequence. */ public static > TSource max( Enumerable source) { Function2 max = maxFunction(); return aggregate(source, null, max); } /** * Invokes a transform function on each element of a * sequence and returns the maximum Decimal value. */ public static BigDecimal max(Enumerable source, BigDecimalFunction1 selector) { Function2 max = maxFunction(); return aggregate(source.select(selector), null, max); } /** * Invokes a transform function on each element of a * sequence and returns the maximum nullable Decimal * value. */ public static BigDecimal max(Enumerable source, NullableBigDecimalFunction1 selector) { Function2 max = maxFunction(); return aggregate(source.select(selector), null, max); } /** * Invokes a transform function on each element of a * sequence and returns the maximum Double value. */ public static double max(Enumerable source, DoubleFunction1 selector) { return aggregate(source.select(adapt(selector)), null, Extensions.DOUBLE_MAX); } /** * Invokes a transform function on each element of a * sequence and returns the maximum nullable Double * value. */ public static Double max(Enumerable source, NullableDoubleFunction1 selector) { return aggregate(source.select(selector), null, Extensions.DOUBLE_MAX); } /** * Invokes a transform function on each element of a * sequence and returns the maximum int value. */ public static int max(Enumerable source, IntegerFunction1 selector) { return aggregate(source.select(adapt(selector)), null, Extensions.INTEGER_MAX); } /** * Invokes a transform function on each element of a * sequence and returns the maximum nullable int value. (Defined * by Enumerable.) */ public static Integer max(Enumerable source, NullableIntegerFunction1 selector) { return aggregate(source.select(selector), null, Extensions.INTEGER_MAX); } /** * Invokes a transform function on each element of a * sequence and returns the maximum long value. */ public static long max(Enumerable source, LongFunction1 selector) { return aggregate(source.select(adapt(selector)), null, Extensions.LONG_MAX); } /** * Invokes a transform function on each element of a * sequence and returns the maximum nullable long value. (Defined * by Enumerable.) */ public static Long max(Enumerable source, NullableLongFunction1 selector) { return aggregate(source.select(selector), null, Extensions.LONG_MAX); } /** * Invokes a transform function on each element of a * sequence and returns the maximum Float value. */ public static float max(Enumerable source, FloatFunction1 selector) { return aggregate(source.select(adapt(selector)), null, Extensions.FLOAT_MAX); } /** * Invokes a transform function on each element of a * sequence and returns the maximum nullable Float * value. */ public static Float max(Enumerable source, NullableFloatFunction1 selector) { return aggregate(source.select(selector), null, Extensions.FLOAT_MAX); } /** * Invokes a transform function on each element of a * generic sequence and returns the maximum resulting * value. */ public static > TResult max( Enumerable source, Function1 selector) { Function2 max = maxFunction(); return aggregate(source.select(selector), null, max); } /** * Returns the minimum value in a generic * sequence. */ public static > TSource min( Enumerable source) { Function2 min = minFunction(); return aggregate(source, null, min); } @SuppressWarnings("unchecked") private static > Function2 minFunction() { return (Function2) (Function2) Extensions.COMPARABLE_MIN; } @SuppressWarnings("unchecked") private static > Function2 maxFunction() { return (Function2) (Function2) Extensions.COMPARABLE_MAX; } /** * Invokes a transform function on each element of a * sequence and returns the minimum Decimal value. */ public static BigDecimal min(Enumerable source, BigDecimalFunction1 selector) { Function2 min = minFunction(); return aggregate(source.select(selector), null, min); } /** * Invokes a transform function on each element of a * sequence and returns the minimum nullable Decimal * value. */ public static BigDecimal min(Enumerable source, NullableBigDecimalFunction1 selector) { Function2 min = minFunction(); return aggregate(source.select(selector), null, min); } /** * Invokes a transform function on each element of a * sequence and returns the minimum Double value. */ public static double min(Enumerable source, DoubleFunction1 selector) { return aggregate(source.select(adapt(selector)), null, Extensions.DOUBLE_MIN); } /** * Invokes a transform function on each element of a * sequence and returns the minimum nullable Double * value. */ public static Double min(Enumerable source, NullableDoubleFunction1 selector) { return aggregate(source.select(selector), null, Extensions.DOUBLE_MIN); } /** * Invokes a transform function on each element of a * sequence and returns the minimum int value. */ public static int min(Enumerable source, IntegerFunction1 selector) { return aggregate(source.select(adapt(selector)), null, Extensions.INTEGER_MIN); } /** * Invokes a transform function on each element of a * sequence and returns the minimum nullable int value. (Defined * by Enumerable.) */ public static Integer min(Enumerable source, NullableIntegerFunction1 selector) { return aggregate(source.select(selector), null, Extensions.INTEGER_MIN); } /** * Invokes a transform function on each element of a * sequence and returns the minimum long value. */ public static long min(Enumerable source, LongFunction1 selector) { return aggregate(source.select(adapt(selector)), null, Extensions.LONG_MIN); } /** * Invokes a transform function on each element of a * sequence and returns the minimum nullable long value. (Defined * by Enumerable.) */ public static Long min(Enumerable source, NullableLongFunction1 selector) { return aggregate(source.select(selector), null, Extensions.LONG_MIN); } /** * Invokes a transform function on each element of a * sequence and returns the minimum Float value. */ public static float min(Enumerable source, FloatFunction1 selector) { return aggregate(source.select(adapt(selector)), null, Extensions.FLOAT_MIN); } /** * Invokes a transform function on each element of a * sequence and returns the minimum nullable Float * value. */ public static Float min(Enumerable source, NullableFloatFunction1 selector) { return aggregate(source.select(selector), null, Extensions.FLOAT_MIN); } /** * Invokes a transform function on each element of a * generic sequence and returns the minimum resulting * value. */ public static > TResult min( Enumerable source, Function1 selector) { Function2 min = minFunction(); return aggregate(source.select(selector), null, min); } /** * Filters the elements of an Enumerable based on a * specified type. * *

Analogous to LINQ's Enumerable.OfType extension method.

* * @param clazz Target type * @param Target type * * @return Collection of T2 */ public static Enumerable ofType( Enumerable enumerable, Class clazz) { //noinspection unchecked return (Enumerable) where(enumerable, Functions.ofTypePredicate(clazz)); } /** * Sorts the elements of a sequence in ascending * order according to a key. */ public static Enumerable orderBy( Enumerable source, Function1 keySelector) { return orderBy(source, keySelector, null); } /** * Sorts the elements of a sequence in ascending * order by using a specified comparer. */ public static Enumerable orderBy( Enumerable source, Function1 keySelector, Comparator comparator) { // NOTE: TreeMap allows null comparator. But the caller of this method // must supply a comparator if the key does not extend Comparable. // Otherwise there will be a ClassCastException while retrieving. final Map> map = new TreeMap<>(comparator); LookupImpl lookup = toLookup_(map, source, keySelector, Functions.identitySelector()); return lookup.valuesEnumerable(); } /** * Sorts the elements of a sequence in descending * order according to a key. */ public static Enumerable orderByDescending( Enumerable source, Function1 keySelector) { return orderBy(source, keySelector, Collections.reverseOrder()); } /** * Sorts the elements of a sequence in descending * order by using a specified comparer. */ public static Enumerable orderByDescending( Enumerable source, Function1 keySelector, Comparator comparator) { return orderBy(source, keySelector, Collections.reverseOrder(comparator)); } /** * Inverts the order of the elements in a * sequence. */ public static Enumerable reverse( Enumerable source) { final List list = toList(source); final int n = list.size(); return Linq4j.asEnumerable( new AbstractList() { public TSource get(int index) { return list.get(n - 1 - index); } public int size() { return n; } }); } /** * Projects each element of a sequence into a new form. */ public static Enumerable select( final Enumerable source, final Function1 selector) { if (selector == Functions.identitySelector()) { //noinspection unchecked return (Enumerable) source; } return new AbstractEnumerable() { public Enumerator enumerator() { return new Enumerator() { final Enumerator enumerator = source.enumerator(); public TResult current() { return selector.apply(enumerator.current()); } public boolean moveNext() { return enumerator.moveNext(); } public void reset() { enumerator.reset(); } public void close() { enumerator.close(); } }; } }; } /** * Projects each element of a sequence into a new * form by incorporating the element's index. */ public static Enumerable select( final Enumerable source, final Function2 selector) { return new AbstractEnumerable() { public Enumerator enumerator() { return new Enumerator() { final Enumerator enumerator = source.enumerator(); int n = -1; public TResult current() { return selector.apply(enumerator.current(), n); } public boolean moveNext() { if (enumerator.moveNext()) { ++n; return true; } else { return false; } } public void reset() { enumerator.reset(); } public void close() { enumerator.close(); } }; } }; } /** * Projects each element of a sequence to an * {@code Enumerable} and flattens the resulting sequences into one * sequence. */ public static Enumerable selectMany( final Enumerable source, final Function1> selector) { return new AbstractEnumerable() { public Enumerator enumerator() { return new Enumerator() { Enumerator sourceEnumerator = source.enumerator(); Enumerator resultEnumerator = Linq4j.emptyEnumerator(); public TResult current() { return resultEnumerator.current(); } public boolean moveNext() { for (;;) { if (resultEnumerator.moveNext()) { return true; } if (!sourceEnumerator.moveNext()) { return false; } resultEnumerator = selector.apply(sourceEnumerator.current()) .enumerator(); } } public void reset() { sourceEnumerator.reset(); resultEnumerator = Linq4j.emptyEnumerator(); } public void close() { sourceEnumerator.close(); resultEnumerator.close(); } }; } }; } /** * Projects each element of a sequence to an * {@code Enumerable}, and flattens the resulting sequences into one * sequence. The index of each source element is used in the * projected form of that element. */ public static Enumerable selectMany( final Enumerable source, final Function2> selector) { return new AbstractEnumerable() { public Enumerator enumerator() { return new Enumerator() { int index = -1; Enumerator sourceEnumerator = source.enumerator(); Enumerator resultEnumerator = Linq4j.emptyEnumerator(); public TResult current() { return resultEnumerator.current(); } public boolean moveNext() { for (;;) { if (resultEnumerator.moveNext()) { return true; } if (!sourceEnumerator.moveNext()) { return false; } index += 1; resultEnumerator = selector.apply(sourceEnumerator.current(), index) .enumerator(); } } public void reset() { sourceEnumerator.reset(); resultEnumerator = Linq4j.emptyEnumerator(); } public void close() { sourceEnumerator.close(); resultEnumerator.close(); } }; } }; } /** * Projects each element of a sequence to an * {@code Enumerable}, flattens the resulting sequences into one * sequence, and invokes a result selector function on each * element therein. The index of each source element is used in * the intermediate projected form of that element. */ public static Enumerable selectMany( final Enumerable source, final Function2> collectionSelector, final Function2 resultSelector) { return new AbstractEnumerable() { public Enumerator enumerator() { return new Enumerator() { int index = -1; Enumerator sourceEnumerator = source.enumerator(); Enumerator collectionEnumerator = Linq4j.emptyEnumerator(); Enumerator resultEnumerator = Linq4j.emptyEnumerator(); public TResult current() { return resultEnumerator.current(); } public boolean moveNext() { for (;;) { if (resultEnumerator.moveNext()) { return true; } if (!sourceEnumerator.moveNext()) { return false; } index += 1; final TSource sourceElement = sourceEnumerator.current(); collectionEnumerator = collectionSelector.apply(sourceElement, index) .enumerator(); resultEnumerator = new TransformedEnumerator(collectionEnumerator) { protected TResult transform(TCollection collectionElement) { return resultSelector.apply(sourceElement, collectionElement); } }; } } public void reset() { sourceEnumerator.reset(); resultEnumerator = Linq4j.emptyEnumerator(); } public void close() { sourceEnumerator.close(); resultEnumerator.close(); } }; } }; } /** * Projects each element of a sequence to an * {@code Enumerable}, flattens the resulting sequences into one * sequence, and invokes a result selector function on each * element therein. */ public static Enumerable selectMany( final Enumerable source, final Function1> collectionSelector, final Function2 resultSelector) { return new AbstractEnumerable() { public Enumerator enumerator() { return new Enumerator() { Enumerator sourceEnumerator = source.enumerator(); Enumerator collectionEnumerator = Linq4j.emptyEnumerator(); Enumerator resultEnumerator = Linq4j.emptyEnumerator(); public TResult current() { return resultEnumerator.current(); } public boolean moveNext() { boolean incremented = false; for (;;) { if (resultEnumerator.moveNext()) { return true; } if (!sourceEnumerator.moveNext()) { return false; } final TSource sourceElement = sourceEnumerator.current(); collectionEnumerator = collectionSelector.apply(sourceElement) .enumerator(); resultEnumerator = new TransformedEnumerator(collectionEnumerator) { protected TResult transform(TCollection collectionElement) { return resultSelector.apply(sourceElement, collectionElement); } }; } } public void reset() { sourceEnumerator.reset(); resultEnumerator = Linq4j.emptyEnumerator(); } public void close() { sourceEnumerator.close(); resultEnumerator.close(); } }; } }; } /** * Determines whether two sequences are equal by * comparing the elements by using the default equality comparer * for their type. */ public static boolean sequenceEqual(Enumerable first, Enumerable second) { return sequenceEqual(first, second, null); } /** * Determines whether two sequences are equal by * comparing their elements by using a specified * {@code EqualityComparer}. */ public static boolean sequenceEqual(Enumerable first, Enumerable second, EqualityComparer comparer) { Objects.requireNonNull(first); Objects.requireNonNull(second); if (comparer == null) { comparer = new EqualityComparer() { public boolean equal(TSource v1, TSource v2) { return Objects.equals(v1, v2); } public int hashCode(TSource tSource) { return Objects.hashCode(tSource); } }; } final CollectionEnumerable firstCollection = first instanceof CollectionEnumerable ? ((CollectionEnumerable) first) : null; if (firstCollection != null) { final CollectionEnumerable secondCollection = second instanceof CollectionEnumerable ? ((CollectionEnumerable) second) : null; if (secondCollection != null) { if (firstCollection.getCollection().size() != secondCollection.getCollection().size()) { return false; } } } try (Enumerator os1 = first.enumerator(); Enumerator os2 = second.enumerator()) { while (os1.moveNext()) { if (!(os2.moveNext() && comparer.equal(os1.current(), os2.current()))) { return false; } } return !os2.moveNext(); } } /** * Returns the only element of a sequence, and throws * an exception if there is not exactly one element in the * sequence. */ public static TSource single(Enumerable source) { TSource toRet = null; try (Enumerator os = source.enumerator()) { if (os.moveNext()) { toRet = os.current(); if (os.moveNext()) { throw new IllegalStateException(); } } if (toRet != null) { return toRet; } throw new IllegalStateException(); } } /** * Returns the only element of a sequence that * satisfies a specified condition, and throws an exception if * more than one such element exists. */ public static TSource single(Enumerable source, Predicate1 predicate) { TSource toRet = null; try (Enumerator os = source.enumerator()) { while (os.moveNext()) { if (predicate.apply(os.current())) { if (toRet == null) { toRet = os.current(); } else { throw new IllegalStateException(); } } } if (toRet != null) { return toRet; } throw new IllegalStateException(); } } /** * Returns the only element of a sequence, or a * default value if the sequence is empty; this method throws an * exception if there is more than one element in the * sequence. */ public static TSource singleOrDefault(Enumerable source) { TSource toRet = null; try (Enumerator os = source.enumerator()) { if (os.moveNext()) { toRet = os.current(); } if (os.moveNext()) { return null; } return toRet; } } /** * Returns the only element of a sequence that * satisfies a specified condition or a default value if no such * element exists; this method throws an exception if more than * one element satisfies the condition. */ public static TSource singleOrDefault(Enumerable source, Predicate1 predicate) { TSource toRet = null; for (TSource s : source) { if (predicate.apply(s)) { if (toRet != null) { return null; } else { toRet = s; } } } return toRet; } /** * Bypasses a specified number of elements in a * sequence and then returns the remaining elements. */ public static Enumerable skip(Enumerable source, final int count) { return skipWhile(source, (v1, v2) -> { // Count is 1-based return v2 < count; }); } /** * Bypasses elements in a sequence as long as a * specified condition is true and then returns the remaining * elements. */ public static Enumerable skipWhile( Enumerable source, Predicate1 predicate) { return skipWhile(source, Functions.toPredicate2(predicate)); } /** * Bypasses elements in a sequence as long as a * specified condition is true and then returns the remaining * elements. The element's index is used in the logic of the * predicate function. */ public static Enumerable skipWhile( final Enumerable source, final Predicate2 predicate) { return new AbstractEnumerable() { public Enumerator enumerator() { return new SkipWhileEnumerator<>(source.enumerator(), predicate); } }; } /** * Computes the sum of the sequence of Decimal values * that are obtained by invoking a transform function on each * element of the input sequence. */ public static BigDecimal sum(Enumerable source, BigDecimalFunction1 selector) { return aggregate(source.select(selector), BigDecimal.ZERO, Extensions.BIG_DECIMAL_SUM); } /** * Computes the sum of the sequence of nullable * Decimal values that are obtained by invoking a transform * function on each element of the input sequence. */ public static BigDecimal sum(Enumerable source, NullableBigDecimalFunction1 selector) { return aggregate(source.select(selector), BigDecimal.ZERO, Extensions.BIG_DECIMAL_SUM); } /** * Computes the sum of the sequence of Double values * that are obtained by invoking a transform function on each * element of the input sequence. */ public static double sum(Enumerable source, DoubleFunction1 selector) { return aggregate(source.select(adapt(selector)), 0d, Extensions.DOUBLE_SUM); } /** * Computes the sum of the sequence of nullable * Double values that are obtained by invoking a transform * function on each element of the input sequence. */ public static Double sum(Enumerable source, NullableDoubleFunction1 selector) { return aggregate(source.select(selector), 0d, Extensions.DOUBLE_SUM); } /** * Computes the sum of the sequence of int values * that are obtained by invoking a transform function on each * element of the input sequence. */ public static int sum(Enumerable source, IntegerFunction1 selector) { return aggregate(source.select(adapt(selector)), 0, Extensions.INTEGER_SUM); } /** * Computes the sum of the sequence of nullable int * values that are obtained by invoking a transform function on * each element of the input sequence. */ public static Integer sum(Enumerable source, NullableIntegerFunction1 selector) { return aggregate(source.select(selector), 0, Extensions.INTEGER_SUM); } /** * Computes the sum of the sequence of long values * that are obtained by invoking a transform function on each * element of the input sequence. */ public static long sum(Enumerable source, LongFunction1 selector) { return aggregate(source.select(adapt(selector)), 0L, Extensions.LONG_SUM); } /** * Computes the sum of the sequence of nullable long * values that are obtained by invoking a transform function on * each element of the input sequence. */ public static Long sum(Enumerable source, NullableLongFunction1 selector) { return aggregate(source.select(selector), 0L, Extensions.LONG_SUM); } /** * Computes the sum of the sequence of Float values * that are obtained by invoking a transform function on each * element of the input sequence. */ public static float sum(Enumerable source, FloatFunction1 selector) { return aggregate(source.select(adapt(selector)), 0F, Extensions.FLOAT_SUM); } /** * Computes the sum of the sequence of nullable * Float values that are obtained by invoking a transform * function on each element of the input sequence. */ public static Float sum(Enumerable source, NullableFloatFunction1 selector) { return aggregate(source.select(selector), 0F, Extensions.FLOAT_SUM); } /** * Returns a specified number of contiguous elements * from the start of a sequence. */ public static Enumerable take(Enumerable source, final int count) { return takeWhile( source, (v1, v2) -> { // Count is 1-based return v2 < count; }); } /** * Returns a specified number of contiguous elements * from the start of a sequence. */ public static Enumerable take(Enumerable source, final long count) { return takeWhileLong( source, (v1, v2) -> { // Count is 1-based return v2 < count; }); } /** * Returns elements from a sequence as long as a * specified condition is true. */ public static Enumerable takeWhile( Enumerable source, final Predicate1 predicate) { return takeWhile(source, Functions.toPredicate2(predicate)); } /** * Returns elements from a sequence as long as a * specified condition is true. The element's index is used in the * logic of the predicate function. */ public static Enumerable takeWhile( final Enumerable source, final Predicate2 predicate) { return new AbstractEnumerable() { public Enumerator enumerator() { return new TakeWhileEnumerator<>(source.enumerator(), predicate); } }; } /** * Returns elements from a sequence as long as a * specified condition is true. The element's index is used in the * logic of the predicate function. */ public static Enumerable takeWhileLong( final Enumerable source, final Predicate2 predicate) { return new AbstractEnumerable() { public Enumerator enumerator() { return new TakeWhileLongEnumerator<>(source.enumerator(), predicate); } }; } /** * Performs a subsequent ordering of the elements in a sequence according * to a key. */ public static OrderedEnumerable createOrderedEnumerable( OrderedEnumerable source, Function1 keySelector, Comparator comparator, boolean descending) { throw Extensions.todo(); } /** * Performs a subsequent ordering of the elements in a sequence in * ascending order according to a key. */ public static > OrderedEnumerable thenBy( OrderedEnumerable source, Function1 keySelector) { return createOrderedEnumerable(source, keySelector, Extensions.comparableComparator(), false); } /** * Performs a subsequent ordering of the elements in a sequence in * ascending order according to a key, using a specified comparator. */ public static OrderedEnumerable thenBy( OrderedEnumerable source, Function1 keySelector, Comparator comparator) { return createOrderedEnumerable(source, keySelector, comparator, false); } /** * Performs a subsequent ordering of the elements in a sequence in * descending order according to a key. */ public static > OrderedEnumerable thenByDescending( OrderedEnumerable source, Function1 keySelector) { return createOrderedEnumerable(source, keySelector, Extensions.comparableComparator(), true); } /** * Performs a subsequent ordering of the elements in a sequence in * descending order according to a key, using a specified comparator. */ public static OrderedEnumerable thenByDescending( OrderedEnumerable source, Function1 keySelector, Comparator comparator) { return createOrderedEnumerable(source, keySelector, comparator, true); } /** * Creates a Map<TKey, TValue> from an * Enumerable<TSource> according to a specified key selector * function. * *

NOTE: Called {@code toDictionary} in LINQ.NET.

*/ public static Map toMap( Enumerable source, Function1 keySelector) { return toMap(source, keySelector, Functions.identitySelector()); } /** * Creates a {@code Map} from an * {@code Enumerable} according to a specified key selector function * and key comparer. */ public static Map toMap( Enumerable source, Function1 keySelector, EqualityComparer comparer) { return toMap(source, keySelector, Functions.identitySelector(), comparer); } /** * Creates a {@code Map} from an * {@code Enumerable} according to specified key selector and element * selector functions. */ public static Map toMap( Enumerable source, Function1 keySelector, Function1 elementSelector) { // Use LinkedHashMap because groupJoin requires order of keys to be // preserved. final Map map = new LinkedHashMap<>(); try (Enumerator os = source.enumerator()) { while (os.moveNext()) { TSource o = os.current(); map.put(keySelector.apply(o), elementSelector.apply(o)); } } return map; } /** * Creates a {@code Map} from an * {@code Enumerable} according to a specified key selector function, * a comparer, and an element selector function. */ public static Map toMap( Enumerable source, Function1 keySelector, Function1 elementSelector, EqualityComparer comparer) { // Use LinkedHashMap because groupJoin requires order of keys to be // preserved. final Map map = new WrapMap<>( // Java 8 cannot infer return type with LinkedHashMap::new is used () -> new LinkedHashMap, TElement>(), comparer); try (Enumerator os = source.enumerator()) { while (os.moveNext()) { TSource o = os.current(); map.put(keySelector.apply(o), elementSelector.apply(o)); } } return map; } /** * Creates a {@code List} from an {@code Enumerable}. */ @SuppressWarnings("unchecked") public static List toList(Enumerable source) { if (source instanceof List && source instanceof RandomAccess) { return (List) source; } else { return source.into( source instanceof Collection ? new ArrayList<>(((Collection) source).size()) : new ArrayList<>()); } } /** * Creates a Lookup<TKey, TElement> from an * Enumerable<TSource> according to a specified key selector * function. */ public static Lookup toLookup( Enumerable source, Function1 keySelector) { return toLookup(source, keySelector, Functions.identitySelector()); } /** * Creates a {@code Lookup} from an * {@code Enumerable} according to a specified key selector function * and key comparer. */ public static Lookup toLookup( Enumerable source, Function1 keySelector, EqualityComparer comparer) { return toLookup( source, keySelector, Functions.identitySelector(), comparer); } /** * Creates a {@code Lookup} from an * {@code Enumerable} according to specified key selector and element * selector functions. */ public static Lookup toLookup( Enumerable source, Function1 keySelector, Function1 elementSelector) { final Map> map = new HashMap<>(); return toLookup_(map, source, keySelector, elementSelector); } static LookupImpl toLookup_( Map> map, Enumerable source, Function1 keySelector, Function1 elementSelector) { try (Enumerator os = source.enumerator()) { while (os.moveNext()) { TSource o = os.current(); final TKey key = keySelector.apply(o); List list = map.get(key); if (list == null) { // for first entry, use a singleton list to save space list = Collections.singletonList(elementSelector.apply(o)); } else { if (list.size() == 1) { // when we go from 1 to 2 elements, switch to array list TElement element = list.get(0); list = new ArrayList<>(); list.add(element); } list.add(elementSelector.apply(o)); } map.put(key, list); } } return new LookupImpl<>(map); } /** * Creates a {@code Lookup} from an * {@code Enumerable} according to a specified key selector function, * a comparer and an element selector function. */ public static Lookup toLookup( Enumerable source, Function1 keySelector, Function1 elementSelector, EqualityComparer comparer) { return toLookup_( new WrapMap<>( // Java 8 cannot infer return type with HashMap::new is used () -> new HashMap, List>(), comparer), source, keySelector, elementSelector); } /** * Produces the set union of two sequences by using * the default equality comparer. */ public static Enumerable union(Enumerable source0, Enumerable source1) { Set set = new HashSet<>(); source0.into(set); source1.into(set); return Linq4j.asEnumerable(set); } /** * Produces the set union of two sequences by using a * specified EqualityComparer<TSource>. */ public static Enumerable union(Enumerable source0, Enumerable source1, final EqualityComparer comparer) { if (comparer == Functions.identityComparer()) { return union(source0, source1); } Set> set = new HashSet<>(); Function1> wrapper = wrapperFor(comparer); Function1, TSource> unwrapper = unwrapper(); source0.select(wrapper).into(set); source1.select(wrapper).into(set); return Linq4j.asEnumerable(set).select(unwrapper); } private static Function1, TSource> unwrapper() { return a0 -> a0.element; } private static Function1> wrapperFor( final EqualityComparer comparer) { return a0 -> Wrapped.upAs(comparer, a0); } /** * Filters a sequence of values based on a * predicate. */ public static Enumerable where( final Enumerable source, final Predicate1 predicate) { assert predicate != null; return new AbstractEnumerable() { public Enumerator enumerator() { final Enumerator enumerator = source.enumerator(); return EnumerableDefaults.where(enumerator, predicate); } }; } private static Enumerator where( final Enumerator enumerator, final Predicate1 predicate) { return new Enumerator() { public TSource current() { return enumerator.current(); } public boolean moveNext() { while (enumerator.moveNext()) { if (predicate.apply(enumerator.current())) { return true; } } return false; } public void reset() { enumerator.reset(); } public void close() { enumerator.close(); } }; } /** * Filters a sequence of values based on a * predicate. Each element's index is used in the logic of the * predicate function. */ public static Enumerable where( final Enumerable source, final Predicate2 predicate) { return new AbstractEnumerable() { public Enumerator enumerator() { return new Enumerator() { final Enumerator enumerator = source.enumerator(); int n = -1; public TSource current() { return enumerator.current(); } public boolean moveNext() { while (enumerator.moveNext()) { ++n; if (predicate.apply(enumerator.current(), n)) { return true; } } return false; } public void reset() { enumerator.reset(); n = -1; } public void close() { enumerator.close(); } }; } }; } /** * Applies a specified function to the corresponding * elements of two sequences, producing a sequence of the * results. */ public static Enumerable zip( final Enumerable first, final Enumerable second, final Function2 resultSelector) { return new AbstractEnumerable() { public Enumerator enumerator() { return new Enumerator() { final Enumerator e1 = first.enumerator(); final Enumerator e2 = second.enumerator(); public TResult current() { return resultSelector.apply(e1.current(), e2.current()); } public boolean moveNext() { return e1.moveNext() && e2.moveNext(); } public void reset() { e1.reset(); e2.reset(); } public void close() { e1.close(); e2.close(); } }; } }; } public static OrderedQueryable asOrderedQueryable( Enumerable source) { //noinspection unchecked return source instanceof OrderedQueryable ? ((OrderedQueryable) source) : new EnumerableOrderedQueryable<>( source, (Class) Object.class, null, null); } /** Default implementation of {@link ExtendedEnumerable#into(Collection)}. */ public static > C into( Enumerable source, C sink) { try (Enumerator enumerator = source.enumerator()) { while (enumerator.moveNext()) { T t = enumerator.current(); sink.add(t); } } return sink; } /** Default implementation of {@link ExtendedEnumerable#removeAll(Collection)}. */ public static > C remove( Enumerable source, C sink) { List tempList = new ArrayList<>(); source.into(tempList); sink.removeAll(tempList); return sink; } /** Enumerable that implements take-while. * * @param element type */ static class TakeWhileEnumerator implements Enumerator { private final Enumerator enumerator; private final Predicate2 predicate; boolean done = false; int n = -1; TakeWhileEnumerator(Enumerator enumerator, Predicate2 predicate) { this.enumerator = enumerator; this.predicate = predicate; } public TSource current() { return enumerator.current(); } public boolean moveNext() { if (!done) { if (enumerator.moveNext() && predicate.apply(enumerator.current(), ++n)) { return true; } else { done = true; } } return false; } public void reset() { enumerator.reset(); done = false; n = -1; } public void close() { enumerator.close(); } } /** Enumerable that implements take-while. * * @param element type */ static class TakeWhileLongEnumerator implements Enumerator { private final Enumerator enumerator; private final Predicate2 predicate; boolean done = false; long n = -1; TakeWhileLongEnumerator(Enumerator enumerator, Predicate2 predicate) { this.enumerator = enumerator; this.predicate = predicate; } public TSource current() { return enumerator.current(); } public boolean moveNext() { if (!done) { if (enumerator.moveNext() && predicate.apply(enumerator.current(), ++n)) { return true; } else { done = true; } } return false; } public void reset() { enumerator.reset(); done = false; n = -1; } public void close() { enumerator.close(); } } /** Enumerator that implements skip-while. * * @param element type */ static class SkipWhileEnumerator implements Enumerator { private final Enumerator enumerator; private final Predicate2 predicate; boolean started = false; int n = -1; SkipWhileEnumerator(Enumerator enumerator, Predicate2 predicate) { this.enumerator = enumerator; this.predicate = predicate; } public TSource current() { return enumerator.current(); } public boolean moveNext() { for (;;) { if (!enumerator.moveNext()) { return false; } if (started) { return true; } if (!predicate.apply(enumerator.current(), ++n)) { started = true; return true; } } } public void reset() { enumerator.reset(); started = false; n = -1; } public void close() { enumerator.close(); } } /** Enumerator that casts each value. * * @param element type */ static class CastingEnumerator implements Enumerator { private final Enumerator enumerator; private final Class clazz; CastingEnumerator(Enumerator enumerator, Class clazz) { this.enumerator = enumerator; this.clazz = clazz; } public T current() { return clazz.cast(enumerator.current()); } public boolean moveNext() { return enumerator.moveNext(); } public void reset() { enumerator.reset(); } public void close() { enumerator.close(); } } /** Value wrapped with a comparer. * * @param element type */ private static class Wrapped { private final EqualityComparer comparer; private final T element; private Wrapped(EqualityComparer comparer, T element) { this.comparer = comparer; this.element = element; } static Wrapped upAs(EqualityComparer comparer, T element) { return new Wrapped<>(comparer, element); } @Override public int hashCode() { return comparer.hashCode(element); } @Override public boolean equals(Object obj) { //noinspection unchecked return obj == this || obj instanceof Wrapped && comparer.equal(element, ((Wrapped) obj).element); } public T unwrap() { return element; } } /** Map that wraps each value. * * @param key type * @param value type */ private static class WrapMap extends AbstractMap { private final Map, V> map; private final EqualityComparer comparer; protected WrapMap(Function0, V>> mapProvider, EqualityComparer comparer) { this.map = mapProvider.apply(); this.comparer = comparer; } @Override public Set> entrySet() { return new AbstractSet>() { @Override public Iterator> iterator() { final Iterator, V>> iterator = map.entrySet().iterator(); return new Iterator>() { public boolean hasNext() { return iterator.hasNext(); } public Entry next() { Entry, V> next = iterator.next(); return new SimpleEntry<>(next.getKey().element, next.getValue()); } public void remove() { iterator.remove(); } }; } @Override public int size() { return map.size(); } }; } @Override public boolean containsKey(Object key) { return map.containsKey(wrap((K) key)); } private Wrapped wrap(K key) { return Wrapped.upAs(comparer, key); } @Override public V get(Object key) { return map.get(wrap((K) key)); } @Override public V put(K key, V value) { return map.put(wrap(key), value); } @Override public V remove(Object key) { return map.remove(wrap((K) key)); } @Override public void clear() { map.clear(); } @Override public Collection values() { return map.values(); } } /** Reads a populated map, applying a selector function. * * @param result type * @param key type * @param accumulator type */ private static class LookupResultEnumerable extends AbstractEnumerable2 { private final Map map; private final Function2 resultSelector; LookupResultEnumerable(Map map, Function2 resultSelector) { this.map = map; this.resultSelector = resultSelector; } public Iterator iterator() { final Iterator> iterator = map.entrySet().iterator(); return new Iterator() { public boolean hasNext() { return iterator.hasNext(); } public TResult next() { final Map.Entry entry = iterator.next(); return resultSelector.apply(entry.getKey(), entry.getValue()); } public void remove() { throw new UnsupportedOperationException(); } }; } } /** Enumerator that performs a merge join on its sorted inputs. * * @param result type * @param left input record type * @param key type * @param right input record type */ private static class MergeJoinEnumerator> implements Enumerator { final List lefts = new ArrayList<>(); final List rights = new ArrayList<>(); private final Enumerator leftEnumerator; private final Enumerator rightEnumerator; private final Function1 outerKeySelector; private final Function1 innerKeySelector; private final Function2 resultSelector; boolean done; Enumerator> cartesians; MergeJoinEnumerator(Enumerator leftEnumerator, Enumerator rightEnumerator, Function1 outerKeySelector, Function1 innerKeySelector, Function2 resultSelector) { this.leftEnumerator = leftEnumerator; this.rightEnumerator = rightEnumerator; this.outerKeySelector = outerKeySelector; this.innerKeySelector = innerKeySelector; this.resultSelector = resultSelector; start(); } private void start() { if (!leftEnumerator.moveNext() || !rightEnumerator.moveNext() || !advance()) { done = true; cartesians = Linq4j.emptyEnumerator(); } } /** Moves to the next key that is present in both sides. Populates * lefts and rights with the rows. Restarts the cross-join * enumerator. */ private boolean advance() { TSource left = leftEnumerator.current(); TKey leftKey = outerKeySelector.apply(left); TInner right = rightEnumerator.current(); TKey rightKey = innerKeySelector.apply(right); for (;;) { int c = leftKey.compareTo(rightKey); if (c == 0) { break; } if (c < 0) { if (!leftEnumerator.moveNext()) { done = true; return false; } left = leftEnumerator.current(); leftKey = outerKeySelector.apply(left); } else { if (!rightEnumerator.moveNext()) { done = true; return false; } right = rightEnumerator.current(); rightKey = innerKeySelector.apply(right); } } lefts.clear(); lefts.add(left); for (;;) { if (!leftEnumerator.moveNext()) { done = true; break; } left = leftEnumerator.current(); TKey leftKey2 = outerKeySelector.apply(left); int c = leftKey.compareTo(leftKey2); if (c != 0) { if (c > 0) { throw new IllegalStateException( "mergeJoin assumes inputs sorted in ascending order, " + "however " + leftKey + " is greater than " + leftKey2); } break; } lefts.add(left); } rights.clear(); rights.add(right); for (;;) { if (!rightEnumerator.moveNext()) { done = true; break; } right = rightEnumerator.current(); TKey rightKey2 = innerKeySelector.apply(right); int c = rightKey.compareTo(rightKey2); if (c != 0) { if (c > 0) { throw new IllegalStateException( "mergeJoin assumes input sorted in ascending order, " + "however " + rightKey + " is greater than " + rightKey2); } break; } rights.add(right); } cartesians = Linq4j.product( ImmutableList.of(Linq4j.enumerator(lefts), Linq4j.enumerator(rights))); return true; } public TResult current() { final List list = cartesians.current(); @SuppressWarnings("unchecked") final TSource left = (TSource) list.get(0); @SuppressWarnings("unchecked") final TInner right = (TInner) list.get(1); return resultSelector.apply(left, right); } public boolean moveNext() { for (;;) { if (cartesians.moveNext()) { return true; } if (done) { return false; } if (!advance()) { return false; } } } public void reset() { done = false; leftEnumerator.reset(); rightEnumerator.reset(); start(); } public void close() { } } private static final Object DUMMY = new Object(); /** * Repeat Union All enumerable: it will evaluate the seed enumerable once, and then * it will start to evaluate the iteration enumerable over and over until either it returns * no results, or an optional maximum numbers of iterations is reached * @param seed seed enumerable * @param iteration iteration enumerable * @param iterationLimit maximum numbers of repetitions for the iteration enumerable * (negative value means no limit) * @param record type */ @SuppressWarnings("unchecked") public static Enumerable repeatUnionAll( Enumerable seed, Enumerable iteration, int iterationLimit) { return new AbstractEnumerable() { @Override public Enumerator enumerator() { return new Enumerator() { private TSource current = (TSource) DUMMY; private boolean seedProcessed = false; private int currentIteration = 0; private final Enumerator seedEnumerator = seed.enumerator(); private Enumerator iterativeEnumerator = null; @Override public TSource current() { if (this.current == DUMMY) { throw new NoSuchElementException(); } return this.current; } @Override public boolean moveNext() { // if we are not done with the seed moveNext on it if (!this.seedProcessed) { if (this.seedEnumerator.moveNext()) { this.current = this.seedEnumerator.current(); return true; } else { this.seedProcessed = true; } } // if we are done with the seed, moveNext on the iterative part while (true) { if (iterationLimit >= 0 && this.currentIteration == iterationLimit) { // max number of iterations reached, we are done this.current = (TSource) DUMMY; return false; } if (this.iterativeEnumerator == null) { this.iterativeEnumerator = iteration.enumerator(); } if (this.iterativeEnumerator.moveNext()) { this.current = this.iterativeEnumerator.current(); return true; } if (this.current == DUMMY) { // current iteration did not return any value, we are done return false; } // current iteration level (which returned some values) is finished, go to next one this.current = (TSource) DUMMY; this.iterativeEnumerator.close(); this.iterativeEnumerator = null; this.currentIteration++; } } @Override public void reset() { this.seedEnumerator.reset(); if (this.iterativeEnumerator != null) { this.iterativeEnumerator.close(); this.iterativeEnumerator = null; } this.currentIteration = 0; } @Override public void close() { this.seedEnumerator.close(); if (this.iterativeEnumerator != null) { this.iterativeEnumerator.close(); } } }; } }; } /** * Lazy read and lazy write spool that stores data into a collection */ @SuppressWarnings("unchecked") public static Enumerable lazyCollectionSpool( Collection outputCollection, Enumerable input) { return new AbstractEnumerable() { @Override public Enumerator enumerator() { return new Enumerator() { private TSource current = (TSource) DUMMY; private final Enumerator inputEnumerator = input.enumerator(); private final Collection collection = outputCollection; private final Collection tempCollection = new ArrayList<>(); @Override public TSource current() { if (this.current == DUMMY) { throw new NoSuchElementException(); } return this.current; } @Override public boolean moveNext() { if (this.inputEnumerator.moveNext()) { this.current = this.inputEnumerator.current(); this.tempCollection.add(this.current); return true; } this.flush(); return false; } private void flush() { this.collection.clear(); this.collection.addAll(this.tempCollection); this.tempCollection.clear(); } @Override public void reset() { this.inputEnumerator.reset(); this.collection.clear(); this.tempCollection.clear(); } @Override public void close() { this.inputEnumerator.close(); } }; } }; } } // End EnumerableDefaults.java