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

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

There is a newer version: 5.4.0
Show 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 com.hazelcast.com.liance with
 * the License.  You may obtain a copy of the License at
 *
 * http://www.apache.com.hazelcast.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 com.hazelcast.org.apache.calcite.linq4j;

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

import com.hazelcast.com.google.com.hazelcast.com.on.base.Supplier;
import com.hazelcast.com.google.com.hazelcast.com.on.base.Suppliers;
import com.hazelcast.com.google.com.hazelcast.com.on.collect.HashMultiset;
import com.hazelcast.com.google.com.hazelcast.com.on.collect.ImmutableList;
import com.hazelcast.com.google.com.hazelcast.com.on.collect.Sets;

import com.hazelcast.org.apiguardian.api.API;

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 com.hazelcast.org.apache.calcite.linq4j.Linq4j.CollectionEnumerable;
import static com.hazelcast.org.apache.calcite.linq4j.Linq4j.ListEnumerable;
import static com.hazelcast.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 com.hazelcast.com.ile-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 com.hazelcast.com.arer. */ 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 com.hazelcast.com.arer) { for (TSource o : enumerable) { if (com.hazelcast.com.arer.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 com.hazelcast.com.are 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 com.hazelcast.com.are values. */ public static Enumerable distinct( Enumerable enumerable, EqualityComparer com.hazelcast.com.arer) { if (com.hazelcast.com.arer == Functions.identityComparer()) { return distinct(enumerable); } final Set> set = new HashSet<>(); Function1> wrapper = wrapperFor(com.hazelcast.com.arer); 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 com.hazelcast.com.arer to com.hazelcast.com.are values, * eliminate duplicates. (Defined by Enumerable.) */ public static Enumerable except( Enumerable source0, Enumerable source1) { return except(source0, source1, false); } /** * Produces the set difference of two sequences by * using the default equality com.hazelcast.com.arer to com.hazelcast.com.are values, * using {@code all} to indicate whether to eliminate duplicates. * (Defined by Enumerable.) */ public static Enumerable except( Enumerable source0, Enumerable source1, boolean all) { Collection collection = all ? HashMultiset.create() : new HashSet<>(); source0.into(collection); try (Enumerator os = source1.enumerator()) { while (os.moveNext()) { TSource o = os.current(); collection.remove(o); } return Linq4j.asEnumerable(collection); } } /** * Produces the set difference of two sequences by * using the specified {@code EqualityComparer} to com.hazelcast.com.are * values, eliminate duplicates. */ public static Enumerable except( Enumerable source0, Enumerable source1, EqualityComparer com.hazelcast.com.arer) { return except(source0, source1, com.hazelcast.com.arer, false); } /** * Produces the set difference of two sequences by * using the specified {@code EqualityComparer} to com.hazelcast.com.are * values, using {@code all} to indicate whether to eliminate duplicates. */ public static Enumerable except( Enumerable source0, Enumerable source1, EqualityComparer com.hazelcast.com.arer, boolean all) { if (com.hazelcast.com.arer == Functions.identityComparer()) { return except(source0, source1, all); } Collection> collection = all ? HashMultiset.create() : new HashSet<>(); Function1> wrapper = wrapperFor(com.hazelcast.com.arer); source0.select(wrapper).into(collection); try (Enumerator> os = source1.select(wrapper).enumerator()) { while (os.moveNext()) { Wrapped o = os.current(); collection.remove(o); } } Function1, TSource> unwrapper = unwrapper(); return Linq4j.asEnumerable(collection).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 com.hazelcast.com.ares the keys by using * a specified com.hazelcast.com.arer. */ public static Enumerable> groupBy( Enumerable enumerable, Function1 keySelector, EqualityComparer com.hazelcast.com.arer) { return enumerable.toLookup(keySelector, com.hazelcast.com.arer); } /** * 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 com.hazelcast.com.ared by using a * com.hazelcast.com.arer and each group's elements are projected by using a * specified function. */ public static Enumerable> groupBy( Enumerable enumerable, Function1 keySelector, Function1 elementSelector, EqualityComparer com.hazelcast.com.arer) { return enumerable.toLookup(keySelector, elementSelector, com.hazelcast.com.arer); } /** * 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 com.hazelcast.com.ared by using a * specified com.hazelcast.com.arer. */ public static Enumerable groupBy( Enumerable enumerable, Function1 keySelector, final Function2, TResult> resultSelector, EqualityComparer com.hazelcast.com.arer) { return enumerable.toLookup(keySelector, com.hazelcast.com.arer) .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 com.hazelcast.com.ared by using a * specified com.hazelcast.com.arer, 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 com.hazelcast.com.arer) { return enumerable.toLookup(keySelector, elementSelector, com.hazelcast.com.arer) .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 com.hazelcast.com.ared by using a * specified com.hazelcast.com.arer. */ public static Enumerable groupBy( Enumerable enumerable, Function1 keySelector, Function0 accumulatorInitializer, Function2 accumulatorAdder, Function2 resultSelector, EqualityComparer com.hazelcast.com.arer) { return groupBy_( new WrapMap<>( // Java 8 cannot infer return type with HashMap::new is used () -> new HashMap, TAccumulate>(), com.hazelcast.com.arer), 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 * com.hazelcast.com.arer is used to com.hazelcast.com.are 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 com.hazelcast.com.are keys. */ public static Enumerable groupJoin( final Enumerable outer, final Enumerable inner, final Function1 outerKeySelector, final Function1 innerKeySelector, final Function2, TResult> resultSelector, final EqualityComparer com.hazelcast.com.arer) { return new AbstractEnumerable() { final Map outerMap = outer.toMap(outerKeySelector, com.hazelcast.com.arer); final Lookup innerLookup = inner.toLookup(innerKeySelector, com.hazelcast.com.arer); 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 com.hazelcast.com.arer to com.hazelcast.com.are values, * eliminate duplicates.(Defined by Enumerable.) */ public static Enumerable intersect( Enumerable source0, Enumerable source1) { return intersect(source0, source1, false); } /** * Produces the set intersection of two sequences by * using the default equality com.hazelcast.com.arer to com.hazelcast.com.are values, * using {@code all} to indicate whether to eliminate duplicates. * (Defined by Enumerable.) */ public static Enumerable intersect( Enumerable source0, Enumerable source1, boolean all) { Collection set1 = all ? HashMultiset.create() : new HashSet<>(); source1.into(set1); Collection resultCollection = all ? HashMultiset.create() : new HashSet<>(); try (Enumerator os = source0.enumerator()) { while (os.moveNext()) { TSource o = os.current(); if (set1.remove(o)) { resultCollection.add(o); } } } return Linq4j.asEnumerable(resultCollection); } /** * Produces the set intersection of two sequences by * using the specified {@code EqualityComparer} to com.hazelcast.com.are * values, eliminate duplicates. */ public static Enumerable intersect( Enumerable source0, Enumerable source1, EqualityComparer com.hazelcast.com.arer) { return intersect(source0, source1, com.hazelcast.com.arer, false); } /** * Produces the set intersection of two sequences by * using the specified {@code EqualityComparer} to com.hazelcast.com.are * values, using {@code all} to indicate whether to eliminate duplicates. */ public static Enumerable intersect( Enumerable source0, Enumerable source1, EqualityComparer com.hazelcast.com.arer, boolean all) { if (com.hazelcast.com.arer == Functions.identityComparer()) { return intersect(source0, source1, all); } Collection> collection = all ? HashMultiset.create() : new HashSet<>(); Function1> wrapper = wrapperFor(com.hazelcast.com.arer); source1.select(wrapper).into(collection); Collection> resultCollection = all ? HashMultiset.create() : new HashSet<>(); try (Enumerator> os = source0.select(wrapper).enumerator()) { while (os.moveNext()) { Wrapped o = os.current(); if (collection.remove(o)) { resultCollection.add(o); } } } Function1, TSource> unwrapper = unwrapper(); return Linq4j.asEnumerable(resultCollection).select(unwrapper); } /** * Correlates the elements of two sequences based on * matching keys. The default equality com.hazelcast.com.arer is used to com.hazelcast.com.are * 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 * com.hazelcast.com.are keys. */ public static Enumerable hashJoin( Enumerable outer, Enumerable inner, Function1 outerKeySelector, Function1 innerKeySelector, Function2 resultSelector, EqualityComparer com.hazelcast.com.arer) { return hashJoin( outer, inner, outerKeySelector, innerKeySelector, resultSelector, com.hazelcast.com.arer, false, false); } /** * Correlates the elements of two sequences based on * matching keys. A specified {@code EqualityComparer} is used to * com.hazelcast.com.are keys. */ public static Enumerable hashJoin( Enumerable outer, Enumerable inner, Function1 outerKeySelector, Function1 innerKeySelector, Function2 resultSelector, EqualityComparer com.hazelcast.com.arer, boolean generateNullsOnLeft, boolean generateNullsOnRight) { return hashEquiJoin_( outer, inner, outerKeySelector, innerKeySelector, resultSelector, com.hazelcast.com.arer, generateNullsOnLeft, generateNullsOnRight); } /** * Correlates the elements of two sequences based on * matching keys. A specified {@code EqualityComparer} is used to * com.hazelcast.com.are 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 com.hazelcast.com.arer, boolean generateNullsOnLeft, boolean generateNullsOnRight, Predicate2 predicate) { if (predicate == null) { return hashEquiJoin_( outer, inner, outerKeySelector, innerKeySelector, resultSelector, com.hazelcast.com.arer, generateNullsOnLeft, generateNullsOnRight); } else { return hashJoinWithPredicate_( outer, inner, outerKeySelector, innerKeySelector, resultSelector, com.hazelcast.com.arer, 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 com.hazelcast.com.arer, final boolean generateNullsOnLeft, final boolean generateNullsOnRight) { return new AbstractEnumerable() { public Enumerator enumerator() { final Lookup innerLookup = com.hazelcast.com.arer == null ? inner.toLookup(innerKeySelector) : inner.toLookup(innerKeySelector, com.hazelcast.com.arer); 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 com.hazelcast.com.arer, 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 = com.hazelcast.com.arer == null ? innerToLookUp.toLookup(innerKeySelector) : innerToLookUp .toLookup(innerKeySelector, com.hazelcast.com.arer); 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 com.hazelcast.com.are 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 com.hazelcast.com.lete 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 com.hazelcast.com.arer) { return semiEquiJoin_(outer, inner, outerKeySelector, innerKeySelector, com.hazelcast.com.arer, false); } public static Enumerable semiJoin( final Enumerable outer, final Enumerable inner, final Function1 outerKeySelector, final Function1 innerKeySelector, final EqualityComparer com.hazelcast.com.arer, final Predicate2 nonEquiPredicate) { return semiJoin(outer, inner, outerKeySelector, innerKeySelector, com.hazelcast.com.arer, 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 com.hazelcast.com.arer) { return semiEquiJoin_(outer, inner, outerKeySelector, innerKeySelector, com.hazelcast.com.arer, true); } public static Enumerable antiJoin( final Enumerable outer, final Enumerable inner, final Function1 outerKeySelector, final Function1 innerKeySelector, final EqualityComparer com.hazelcast.com.arer, final Predicate2 nonEquiPredicate) { return semiJoin(outer, inner, outerKeySelector, innerKeySelector, com.hazelcast.com.arer, 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 com.hazelcast.com.are 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 com.hazelcast.com.arer, final boolean anti, final Predicate2 nonEquiPredicate) { if (nonEquiPredicate == null) { return semiEquiJoin_(outer, inner, outerKeySelector, innerKeySelector, com.hazelcast.com.arer, anti); } else { return semiJoinWithPredicate_(outer, inner, outerKeySelector, innerKeySelector, com.hazelcast.com.arer, anti, nonEquiPredicate); } } private static Enumerable semiJoinWithPredicate_( final Enumerable outer, final Enumerable inner, final Function1 outerKeySelector, final Function1 innerKeySelector, final EqualityComparer com.hazelcast.com.arer, final boolean anti, final Predicate2 nonEquiPredicate) { return new AbstractEnumerable() { public Enumerator enumerator() { // CALCITE-2909 Delay the com.hazelcast.com.utation 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( () -> com.hazelcast.com.arer == null ? inner.toLookup(innerKeySelector) : inner.toLookup(innerKeySelector, com.hazelcast.com.arer)); 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 com.hazelcast.com.are keys. */ private static Enumerable semiEquiJoin_( final Enumerable outer, final Enumerable inner, final Function1 outerKeySelector, final Function1 innerKeySelector, final EqualityComparer com.hazelcast.com.arer, final boolean anti) { return new AbstractEnumerable() { public Enumerator enumerator() { // CALCITE-2909 Delay the com.hazelcast.com.utation 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(() -> com.hazelcast.com.arer == null ? inner.select(innerKeySelector).distinct() : inner.select(innerKeySelector).distinct(com.hazelcast.com.arer)); 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) { if (!joinType.generatesNullsOnLeft()) { return nestedLoopJoinOptimized(outer, inner, predicate, resultSelector, joinType); } return nestedLoopJoinAsList(outer, inner, predicate, resultSelector, joinType); } /** * Implementation of nested loop join that builds the com.hazelcast.com.lete result as a list * and then returns it. This is an easy-to-implement solution, but hogs memory. */ private static Enumerable nestedLoopJoinAsList( final Enumerable outer, final Enumerable inner, final Predicate2 predicate, Function2 resultSelector, final JoinType joinType) { final boolean generateNullsOnLeft = joinType.generatesNullsOnLeft(); final boolean generateNullsOnRight = joinType.generatesNullsOnRight(); final List result = new ArrayList<>(); final List rightList = inner.toList(); final Set rightUnmatched; if (generateNullsOnLeft) { rightUnmatched = Sets.newIdentityHashSet(); rightUnmatched.addAll(rightList); } else { rightUnmatched = null; } try (Enumerator lefts = outer.enumerator()) { while (lefts.moveNext()) { int leftMatchCount = 0; final TSource left = lefts.current(); for (TInner right : rightList) { 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 (joinType == JoinType.SEMI) { break; } } } } if (leftMatchCount == 0 && (generateNullsOnRight || joinType == JoinType.ANTI)) { result.add(resultSelector.apply(left, null)); } } if (rightUnmatched != null) { for (TInner right : rightUnmatched) { result.add(resultSelector.apply(null, right)); } } return Linq4j.asEnumerable(result); } } /** * Implementation of nested loop join that, unlike {@link #nestedLoopJoinAsList}, does not * require to build the com.hazelcast.com.lete result as a list before returning it. Instead, it iterates * through the outer enumerable and inner enumerable and returns the results step by step. * It does not support RIGHT / FULL join. */ private static Enumerable nestedLoopJoinOptimized( final Enumerable outer, final Enumerable inner, final Predicate2 predicate, Function2 resultSelector, final JoinType joinType) { if (joinType == JoinType.RIGHT || joinType == JoinType.FULL) { throw new IllegalArgumentException("JoinType " + joinType + " is unsupported"); } return new AbstractEnumerable() { public Enumerator enumerator() { return new Enumerator() { private Enumerator outerEnumerator = outer.enumerator(); private Enumerator innerEnumerator = null; private boolean outerMatch = false; // whether the outerValue has matched an innerValue private TSource outerValue; private TInner innerValue; private int state = 0; // 0 moving outer, 1 moving inner @Override public TResult current() { return resultSelector.apply(outerValue, innerValue); } @Override public boolean moveNext() { while (true) { switch (state) { case 0: // move outer if (!outerEnumerator.moveNext()) { return false; } outerValue = outerEnumerator.current(); closeInner(); innerEnumerator = inner.enumerator(); outerMatch = false; state = 1; continue; case 1: // move inner if (innerEnumerator.moveNext()) { innerValue = innerEnumerator.current(); if (predicate.apply(outerValue, innerValue)) { outerMatch = true; switch (joinType) { case ANTI: // try next outer row state = 0; continue; case SEMI: // return result, and try next outer row state = 0; return true; case INNER: case LEFT: // INNER and LEFT just return result return true; } } // else (predicate returned false) continue: move inner } else { // innerEnumerator is over state = 0; innerValue = null; if (!outerMatch && (joinType == JoinType.LEFT || joinType == JoinType.ANTI)) { // No match detected: outerValue is a result for LEFT / ANTI join return true; } } } } } @Override public void reset() { state = 0; outerMatch = false; outerEnumerator.reset(); closeInner(); } @Override public void close() { outerEnumerator.close(); closeInner(); } private void closeInner() { if (innerEnumerator != null) { innerEnumerator.close(); innerEnumerator = null; } } }; } }; } /** * Joins two inputs that are sorted on the key. * Inputs must sorted in ascending order, nulls last. * @deprecated Use {@link #mergeJoin(Enumerable, Enumerable, Function1, Function1, Function2, JoinType, Comparator)} */ @Deprecated // to be removed before 2.0 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 mergeJoin(outer, inner, outerKeySelector, innerKeySelector, null, resultSelector, JoinType.INNER, null); } /** * Returns if certain join type is supported by Enumerable Merge Join implementation. *

NOTE: This method is subject to change or be removed without notice. */ public static boolean isMergeJoinSupported(JoinType joinType) { switch (joinType) { case INNER: case SEMI: return true; default: return false; } } /** * Joins two inputs that are sorted on the key. * Inputs must sorted in ascending order, nulls last. */ public static , TResult> Enumerable mergeJoin(final Enumerable outer, final Enumerable inner, final Function1 outerKeySelector, final Function1 innerKeySelector, final Function2 resultSelector, final JoinType joinType, final Comparator com.hazelcast.com.arator) { return mergeJoin(outer, inner, outerKeySelector, innerKeySelector, null, resultSelector, joinType, com.hazelcast.com.arator); } /** * Joins two inputs that are sorted on the key, with an extra predicate for non equi-join * conditions. * Inputs must sorted in ascending order, nulls last. * * @param extraPredicate predicate for non equi-join conditions. In case of equi-join, * it will be null. In case of non-equi join, the non-equi conditions * will be evaluated using this extra predicate within the join process, * and not applying a filter on top of the join results, because the latter * strategy can only work on inner joins, and we aim to support other join * types in the future (e.g. semi or anti joins). * @param com.hazelcast.com.arator key com.hazelcast.com.arator, possibly null (in which case {@link Comparable#com.hazelcast.com.areTo} * will be used). * * NOTE: The current API is experimental and subject to change without notice. */ @API(since = "1.23", status = API.Status.EXPERIMENTAL) public static , TResult> Enumerable mergeJoin(final Enumerable outer, final Enumerable inner, final Function1 outerKeySelector, final Function1 innerKeySelector, final Predicate2 extraPredicate, final Function2 resultSelector, final JoinType joinType, final Comparator com.hazelcast.com.arator) { if (!isMergeJoinSupported(joinType)) { throw new UnsupportedOperationException("MergeJoin unsupported for join type " + joinType); } return new AbstractEnumerable() { public Enumerator enumerator() { return new MergeJoinEnumerator<>(outer, inner, outerKeySelector, innerKeySelector, extraPredicate, resultSelector, joinType, com.hazelcast.com.arator); } }; } /** * 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 com.hazelcast.com.arer. */ public static Enumerable orderBy( Enumerable source, Function1 keySelector, Comparator com.hazelcast.com.arator) { return new AbstractEnumerable() { @Override public Enumerator enumerator() { // NOTE: TreeMap allows null com.hazelcast.com.arator. But the caller of this method // must supply a com.hazelcast.com.arator if the key does not extend Comparable. // Otherwise there will be a ClassCastException while retrieving. final Map> map = new TreeMap<>(com.hazelcast.com.arator); final LookupImpl lookup = toLookup_(map, source, keySelector, Functions.identitySelector()); return lookup.valuesEnumerable().enumerator(); } }; } /** * 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 com.hazelcast.com.arer. */ public static Enumerable orderByDescending( Enumerable source, Function1 keySelector, Comparator com.hazelcast.com.arator) { return orderBy(source, keySelector, Collections.reverseOrder(com.hazelcast.com.arator)); } /** * 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 * com.hazelcast.com.aring the elements by using the default equality com.hazelcast.com.arer * for their type. */ public static boolean sequenceEqual(Enumerable first, Enumerable second) { return sequenceEqual(first, second, null); } /** * Determines whether two sequences are equal by * com.hazelcast.com.aring their elements by using a specified * {@code EqualityComparer}. */ public static boolean sequenceEqual(Enumerable first, Enumerable second, EqualityComparer com.hazelcast.com.arer) { Objects.requireNonNull(first); Objects.requireNonNull(second); if (com.hazelcast.com.arer == null) { com.hazelcast.com.arer = 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() && com.hazelcast.com.arer.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 com.hazelcast.com.arator, 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.com.hazelcast.com.arableComparator(), false); } /** * Performs a subsequent ordering of the elements in a sequence in * ascending order according to a key, using a specified com.hazelcast.com.arator. */ public static OrderedEnumerable thenBy( OrderedEnumerable source, Function1 keySelector, Comparator com.hazelcast.com.arator) { return createOrderedEnumerable(source, keySelector, com.hazelcast.com.arator, 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.com.hazelcast.com.arableComparator(), true); } /** * Performs a subsequent ordering of the elements in a sequence in * descending order according to a key, using a specified com.hazelcast.com.arator. */ public static OrderedEnumerable thenByDescending( OrderedEnumerable source, Function1 keySelector, Comparator com.hazelcast.com.arator) { return createOrderedEnumerable(source, keySelector, com.hazelcast.com.arator, 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 com.hazelcast.com.arer. */ public static Map toMap( Enumerable source, Function1 keySelector, EqualityComparer com.hazelcast.com.arer) { return toMap(source, keySelector, Functions.identitySelector(), com.hazelcast.com.arer); } /** * 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 com.hazelcast.com.arer, and an element selector function. */ public static Map toMap( Enumerable source, Function1 keySelector, Function1 elementSelector, EqualityComparer com.hazelcast.com.arer) { // 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>(), com.hazelcast.com.arer); 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 com.hazelcast.com.arer. */ public static Lookup toLookup( Enumerable source, Function1 keySelector, EqualityComparer com.hazelcast.com.arer) { return toLookup( source, keySelector, Functions.identitySelector(), com.hazelcast.com.arer); } /** * 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 com.hazelcast.com.arer and an element selector function. */ public static Lookup toLookup( Enumerable source, Function1 keySelector, Function1 elementSelector, EqualityComparer com.hazelcast.com.arer) { return toLookup_( new WrapMap<>( // Java 8 cannot infer return type with HashMap::new is used () -> new HashMap, List>(), com.hazelcast.com.arer), source, keySelector, elementSelector); } /** * Produces the set union of two sequences by using * the default equality com.hazelcast.com.arer. */ 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 com.hazelcast.com.arer) { if (com.hazelcast.com.arer == Functions.identityComparer()) { return union(source0, source1); } Set> set = new HashSet<>(); Function1> wrapper = wrapperFor(com.hazelcast.com.arer); 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 com.hazelcast.com.arer) { return a0 -> Wrapped.upAs(com.hazelcast.com.arer, 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 com.hazelcast.com.arer. * * @param element type */ private static class Wrapped { private final EqualityComparer com.hazelcast.com.arer; private final T element; private Wrapped(EqualityComparer com.hazelcast.com.arer, T element) { this.com.hazelcast.com.arer = com.hazelcast.com.arer; this.element = element; } static Wrapped upAs(EqualityComparer com.hazelcast.com.arer, T element) { return new Wrapped<>(com.hazelcast.com.arer, element); } @Override public int hashCode() { return com.hazelcast.com.arer.hashCode(element); } @Override public boolean equals(Object obj) { //noinspection unchecked return obj == this || obj instanceof Wrapped && com.hazelcast.com.arer.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 com.hazelcast.com.arer; protected WrapMap(Function0, V>> mapProvider, EqualityComparer com.hazelcast.com.arer) { this.map = mapProvider.apply(); this.com.hazelcast.com.arer = com.hazelcast.com.arer; } @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(com.hazelcast.com.arer, 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. * Inputs must sorted in ascending order, nulls last. * * @param result type * @param left input record type * @param key type * @param right input record type */ private static class MergeJoinEnumerator> implements Enumerator { private final List lefts = new ArrayList<>(); private final List rights = new ArrayList<>(); private final Enumerable leftEnumerable; private final Enumerable rightEnumerable; private Enumerator leftEnumerator = null; private Enumerator rightEnumerator = null; private final Function1 outerKeySelector; private final Function1 innerKeySelector; // extra predicate in case of non equi-join, in case of equi-join it will be null private final Predicate2 extraPredicate; private final Function2 resultSelector; private final JoinType joinType; // key com.hazelcast.com.arator, possibly null (Comparable#com.hazelcast.com.areTo to be used in that case) private final Comparator com.hazelcast.com.arator; private boolean done; private Enumerator results; MergeJoinEnumerator(Enumerable leftEnumerable, Enumerable rightEnumerable, Function1 outerKeySelector, Function1 innerKeySelector, Predicate2 extraPredicate, Function2 resultSelector, JoinType joinType, Comparator com.hazelcast.com.arator) { this.leftEnumerable = leftEnumerable; this.rightEnumerable = rightEnumerable; this.outerKeySelector = outerKeySelector; this.innerKeySelector = innerKeySelector; this.extraPredicate = extraPredicate; this.resultSelector = resultSelector; this.joinType = joinType; this.com.hazelcast.com.arator = com.hazelcast.com.arator; start(); } private Enumerator startLeftEnumerator() { if (leftEnumerator == null) { leftEnumerator = leftEnumerable.enumerator(); } return leftEnumerator; } private Enumerator startRightEnumerator() { if (rightEnumerator == null) { rightEnumerator = rightEnumerable.enumerator(); } return rightEnumerator; } private void start() { if (!startLeftEnumerator().moveNext() || !startRightEnumerator().moveNext() || !advance()) { done = true; results = Linq4j.emptyEnumerator(); } } private int com.hazelcast.com.are(TKey key1, TKey key2) { return com.hazelcast.com.arator != null ? com.hazelcast.com.arator.com.hazelcast.com.are(key1, key2) : key1.com.hazelcast.com.areTo(key2); } /** 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 (;;) { // mergeJoin assumes inputs sorted in ascending order with nulls last, // if we reach a null key, we are done. if (leftKey == null || rightKey == null) { done = true; return false; } int c = com.hazelcast.com.are(leftKey, 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); if (leftKey2 == null) { done = true; break; } int c = com.hazelcast.com.are(leftKey, 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); if (rightKey2 == null) { done = true; break; } int c = com.hazelcast.com.are(rightKey, 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); } if (extraPredicate == null) { // SEMI join must not have duplicates, in that case take just one element from rights results = joinType == JoinType.SEMI ? new CartesianProductJoinEnumerator<>(resultSelector, Linq4j.enumerator(lefts), Linq4j.enumerator(Collections.singletonList(this.rights.get(0)))) : new CartesianProductJoinEnumerator<>(resultSelector, Linq4j.enumerator(lefts), Linq4j.enumerator(rights)); } else { // we must verify the non equi-join predicate, use nested loop join for that results = nestedLoopJoin(Linq4j.asEnumerable(lefts), Linq4j.asEnumerable(rights), extraPredicate, resultSelector, joinType).enumerator(); } return true; } public TResult current() { return results.current(); } public boolean moveNext() { for (;;) { if (results.moveNext()) { return true; } if (done) { return false; } if (!advance()) { return false; } } } public void reset() { done = false; leftEnumerator.reset(); if (rightEnumerator != null) { rightEnumerator.reset(); } start(); } public void close() { leftEnumerator.close(); if (rightEnumerator != null) { rightEnumerator.close(); } } } private static class CartesianProductJoinEnumerator extends CartesianProductEnumerator { private final Function2 resultSelector; @SuppressWarnings("unchecked") CartesianProductJoinEnumerator(Function2 resultSelector, Enumerator outer, Enumerator inner) { super(ImmutableList.of((Enumerator) outer, (Enumerator) inner)); this.resultSelector = resultSelector; } @SuppressWarnings("unchecked") @Override public TResult current() { final TOuter outer = (TOuter) elements[0]; final TInner inner = (TInner) elements[1]; return this.resultSelector.apply(outer, inner); } } private static final Object DUMMY = new Object(); /** * Repeat Union 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 all whether duplicates will be considered or not * @param com.hazelcast.com.arer {@link EqualityComparer} to control duplicates, * only used if {@code all} is {@code false} * @param record type */ @SuppressWarnings("unchecked") public static Enumerable repeatUnion( Enumerable seed, Enumerable iteration, int iterationLimit, boolean all, EqualityComparer com.hazelcast.com.arer) { 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; // Set to control duplicates, only used if "all" is false private final Set> processed = new HashSet<>(); private final Function1> wrapper = wrapperFor(com.hazelcast.com.arer); @Override public TSource current() { if (current == DUMMY) { throw new NoSuchElementException(); } return current; } private boolean checkValue(TSource value) { if (all) { return true; // no need to check duplicates } // check duplicates final Wrapped wrapped = wrapper.apply(value); if (!processed.contains(wrapped)) { processed.add(wrapped); return true; } return false; } @Override public boolean moveNext() { // if we are not done with the seed moveNext on it while (!seedProcessed) { if (seedEnumerator.moveNext()) { TSource value = seedEnumerator.current(); if (checkValue(value)) { current = value; return true; } } else { seedProcessed = true; } } // if we are done with the seed, moveNext on the iterative part while (true) { if (iterationLimit >= 0 && currentIteration == iterationLimit) { // max number of iterations reached, we are done current = (TSource) DUMMY; return false; } if (iterativeEnumerator == null) { iterativeEnumerator = iteration.enumerator(); } while (iterativeEnumerator.moveNext()) { TSource value = iterativeEnumerator.current(); if (checkValue(value)) { current = value; return true; } } if (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 current = (TSource) DUMMY; iterativeEnumerator.close(); iterativeEnumerator = null; currentIteration++; } } @Override public void reset() { seedEnumerator.reset(); seedProcessed = false; processed.clear(); if (iterativeEnumerator != null) { iterativeEnumerator.close(); iterativeEnumerator = null; } currentIteration = 0; } @Override public void close() { seedEnumerator.close(); if (iterativeEnumerator != null) { 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 (current == DUMMY) { throw new NoSuchElementException(); } return current; } @Override public boolean moveNext() { if (inputEnumerator.moveNext()) { current = inputEnumerator.current(); tempCollection.add(current); return true; } flush(); return false; } private void flush() { collection.clear(); collection.addAll(tempCollection); tempCollection.clear(); } @Override public void reset() { inputEnumerator.reset(); collection.clear(); tempCollection.clear(); } @Override public void close() { inputEnumerator.close(); } }; } }; } }