ix.internal.operators.Interactive Maven / Gradle / Ivy
Show all versions of ixjava Show documentation
/*
* Copyright 2011-2016 David Karnok
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ix.internal.operators;
import ix.CloseableIterable;
import ix.CloseableIterator;
import ix.Enumerable;
import ix.Enumerator;
import ix.GroupedIterable;
import ix.Pair;
import ix.internal.util.IxHelperFunctions;
import ix.internal.util.LinkedBuffer;
import java.io.Closeable;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import rx.Notification;
import rx.Scheduler;
import rx.Subscription;
import rx.exceptions.Exceptions;
import rx.functions.Action0;
import rx.functions.Action1;
import rx.functions.Actions;
import rx.functions.Func0;
import rx.functions.Func1;
import rx.functions.Func2;
import rx.schedulers.Schedulers;
/**
* The interactive (i.e., Iterable
based) counterparts
* of the Reactive
operators.
* The implementations of the operators are partially derived
* from the Reactive operators.
* @see rx.Observable
*/
public final class Interactive {
/**
* Creates an iterable which traverses the source iterable and maintains a running sum value based
* on the sum
function parameter. Once the source is depleted, it
* applies the divide
function and returns its result.
* This operator is a general base for averaging (where {@code sum(u, t) => u + t}, {@code divide(u, index) => u / index}),
* summing (where {@code sum(u, t) => u + t}, and {@code divide(u, index) => u)}),
* minimum, maximum, etc.
* If the traversal of the source fails due an exception, that exception is reflected on the
* {@code next()} call of the returned iterator.
* The returned iterator will throw an UnsupportedOperationException
* for its remove()
method.
* @param the source element type
* @param the intermediate aggregation type
* @param the resulting aggregation type
* @param source the source of Ts
* @param sum the function which takes the current intermediate value,
* the current source value and should produce a new intermediate value.
* for the first element of T, the U parameter will receive null
* @param divide the function which takes the last intermediate value and a total count of Ts seen and should return the final aggregation value.
* @return the new iterable
*/
public static Iterable aggregate(
final Iterable extends T> source,
final Func2 super U, ? super T, ? extends U> sum,
final Func2 super U, ? super Integer, ? extends V> divide) {
return new AggregateIterable(source, sum, divide);
}
/**
* Returns an iterable which contains true if all
* elements of the source iterable satisfy the predicate.
* The operator might return a false before fully iterating the source.
* The returned iterator will throw an UnsupportedOperationException
* for its remove()
method.
* @param the source element type
* @param source the source of Ts
* @param predicate the predicate
* @return the new iterable
*/
public static Iterable all(
final Iterable extends T> source,
final Func1 super T, Boolean> predicate) {
return new AllIterable(source, predicate);
}
/**
* Tests if there is any element of the source that satisfies the given predicate function.
* @param the source element type
* @param source the source of Ts
* @param predicate the predicate tester function
* @return the new iterable
*/
public static Iterable any(
final Iterable extends T> source,
final Func1 super T, Boolean> predicate) {
return any(filter(source, predicate));
}
/**
* Determines if the given source has any elements at all.
* The returned iterator will throw an UnsupportedOperationException
* for its remove()
method.
* @param the source element type, irrelevant here
* @param source the source of Ts
* @return the new iterable with a single true or false
*/
public static Iterable any(
final Iterable source) {
return new AnyIterable(source);
}
/**
* Returns a pair of the maximum argument and value from the given sequence.
* @param the element type of the sequence
* @param the value type for the comparison, must be self comparable
* @param source the source sequence
* @param valueSelector the value selector function
* @return the pair of the first maximum element and value, null if the sequence was empty
*/
public static > Pair argAndMax(
Iterable extends T> source,
Func1 super T, ? extends V> valueSelector) {
return argAndMax(source, valueSelector, IxHelperFunctions.comparator());
}
/**
* Returns a pair of the maximum argument and value from the given sequence.
* @param the element type
* @param the value type
* @param source the source sequence of Ts
* @param valueSelector the selector to extract the value from T
* @param valueComparator the comparator to compare two values
* @return the first pair of max argument and value or null if the source sequence was empty
*/
public static Pair argAndMax(
Iterable extends T> source,
Func1 super T, ? extends V> valueSelector,
Comparator super V> valueComparator) {
T arg = null;
V max = null;
boolean hasElement = false;
Iterator extends T> it = source.iterator();
try {
while (it.hasNext()) {
T item = it.next();
V itemValue = valueSelector.call(item);
if (!hasElement || valueComparator.compare(max, itemValue) < 0) {
arg = item;
max = itemValue;
}
hasElement = true;
}
if (hasElement) {
return Pair.of(arg, max);
}
} finally {
unsubscribe(it);
}
return null;
}
/**
* Returns a pair of the maximum argument and value from the given sequence.
* @param the element type of the sequence
* @param the value type for the comparison, must be self comparable
* @param source the source sequence
* @param valueSelector the value selector function
* @return the pair of the first maximum element and value, null if the sequence was empty
*/
public static > Pair argAndMin(
Iterable extends T> source,
Func1 super T, ? extends V> valueSelector) {
return argAndMin(source, valueSelector, IxHelperFunctions.comparator());
}
/**
* Returns a pair of the minimum argument and value from the given sequence.
* @param the element type
* @param the value type
* @param source the source sequence of Ts
* @param valueSelector the selector to extract the value from T
* @param valueComparator the comparator to compare two values
* @return the first pair of min argument and value or null if the source sequence was empty
*/
public static Pair argAndMin(
Iterable extends T> source,
Func1 super T, ? extends V> valueSelector,
final Comparator super V> valueComparator) {
return argAndMax(source, valueSelector, new Comparator() {
@Override
public int compare(V o1, V o2) {
return valueComparator.compare(o2, o1);
}
});
}
/**
* Returns an iterable which averages the source BigDecimal values.
* The returned iterator will throw an UnsupportedOperationException
* for its remove()
method.
* @param source the source of BigDecimal values
* @return the new iterable
*/
public static Iterable averageBigDecimal(
Iterable source) {
return aggregate(source,
new Func2() {
@Override
public BigDecimal call(BigDecimal param1, BigDecimal param2) {
return param1 != null ? param1.add(param2) : param2;
}
},
new Func2() {
@Override
public BigDecimal call(BigDecimal param1, Integer param2) {
return param1.divide(new BigDecimal(param2), BigDecimal.ROUND_HALF_UP);
}
}
);
}
/**
* Returns an iterable which averages the source BigInteger values.
* The returned iterator will throw an UnsupportedOperationException
* for its remove()
method.
* @param source the source of BigInteger values
* @return the new iterable
*/
public static Iterable averageBigInteger(
Iterable source) {
return aggregate(source,
new Func2() {
@Override
public BigInteger call(BigInteger param1, BigInteger param2) {
return param1 != null ? param1.add(param2) : param2;
}
},
new Func2() {
@Override
public BigDecimal call(BigInteger param1, Integer param2) {
return new BigDecimal(param1).divide(new BigDecimal(param2), BigDecimal.ROUND_HALF_UP);
}
}
);
}
/**
* Returns an iterable which averages the source Double values.
* The returned iterator will throw an UnsupportedOperationException
* for its remove()
method.
* @param source the source of Double values
* @return the new iterable
*/
public static Iterable averageDouble(
Iterable source) {
return aggregate(source,
new Func2() {
@Override
public Double call(Double param1, Double param2) {
return param1 != null ? param1 + param2 : param2.doubleValue();
}
},
new Func2() {
@Override
public Double call(Double param1, Integer param2) {
return param1 / param2;
}
}
);
}
/**
* Returns an iterable which averages the source Float values.
* The returned iterator will throw an UnsupportedOperationException
* for its remove()
method.
* @param source the source of Float values
* @return the new iterable
*/
public static Iterable averageFloat(
Iterable source) {
return aggregate(source,
new Func2() {
@Override
public Float call(Float param1, Float param2) {
return param1 != null ? param1 + param2 : param2.floatValue();
}
},
new Func2() {
@Override
public Float call(Float param1, Integer param2) {
return param1 / param2;
}
}
);
}
/**
* Returns an iterable which averages the source Integer values.
* The returned iterator will throw an UnsupportedOperationException
* for its remove()
method.
* @param source the source of Integer values
* @return the new iterable
*/
public static Iterable averageInt(
Iterable source) {
return aggregate(source,
new Func2() {
@Override
public Double call(Double param1, Integer param2) {
return param1 != null ? param1 + param2 : param2.doubleValue();
}
},
new Func2() {
@Override
public Double call(Double param1, Integer param2) {
return param1 / param2;
}
}
);
}
/**
* Returns an iterable which averages the source Integer values.
* The returned iterator will throw an UnsupportedOperationException
* for its remove()
method.
* @param source the source of Integer values
* @return the new iterable
*/
public static Iterable averageLong(
Iterable source) {
return aggregate(source,
new Func2() {
@Override
public Double call(Double param1, Long param2) {
return param1 != null ? param1 + param2 : param2.doubleValue();
}
},
new Func2() {
@Override
public Double call(Double param1, Integer param2) {
return param1 / param2;
}
}
);
}
/**
* Returns an iterable which buffers the source elements
* into bufferSize
lists.
* FIXME what to do on empty source or last chunk?
* The returned iterator will throw an UnsupportedOperationException
* for its remove()
method.
* @param the source element type
* @param source the source of Ts
* @param bufferSize the buffer size.
* @return the new iterable
*/
public static Iterable> buffer(
final Iterable extends T> source,
final int bufferSize) {
if (bufferSize <= 0) {
throw new IllegalArgumentException("bufferSize <= 0");
}
return new BufferIterable(source, bufferSize);
}
/**
* Concatenate the given iterable sources one
* after another in a way, that calling the second iterator()
* only happens when there is no more element in the first iterator.
* The returned iterator forwards all remove()
calls
* to the current source (e.g., you can remove the same elements from
* multiple collections with a single traversal on the concat result).
* @param the element type
* @param sources the list of iterables to concatenate
* @return a new iterable
*/
public static Iterable concat(
final Iterable extends Iterable extends T>> sources) {
return new ConcatIterable(sources);
}
/**
* Concatenate the given iterable sources one
* after another in a way, that calling the second iterator()
* only happens when there is no more element in the first iterator.
* The returned iterator forwards all remove()
calls
* to the current source (first or next).
* @param the element type
* @param first the first iterable
* @param second the second iterable
* @return the new iterable
*/
public static Iterable concat(
final Iterable extends T> first,
final Iterable extends T> second) {
List> list = new LinkedList>();
list.add(first);
list.add(second);
return concat(list);
}
/**
* Returns an iterable which checks for the existence of the supplied
* value by comparing the elements of the source iterable using reference
* and equals()
. The iterable then returns a single true or false.
* @param the source element type
* @param source the source
* @param value the value to check
* @return the new iterable
*/
public static Iterable contains(
final Iterable extends T> source, final Object value) {
return any(source, new Func1() {
@Override
public Boolean call(T param1) {
return param1 == value || (param1 != null && param1.equals(value));
}
});
}
/**
* Counts the elements of the iterable source by using a 32 bit int
.
* The returned iterator will throw an UnsupportedOperationException
* for its remove()
method.
* @param the element type
* @param source the source iterable
* @return the new iterable
*/
public static Iterable count(
final Iterable source) {
return new CountIterable(source);
}
/**
* Counts the elements of the iterable source by using a 64 bit long
.
* The returned iterator will throw an UnsupportedOperationException
* for its remove()
method.
* @param the element type
* @param source the source iterable
* @return the new iterable
*/
public static Iterable countLong(
final Iterable source) {
return new LongCountIterable(source);
}
/**
* Defers the source iterable creation to registration time and
* calls the given func
for the actual source.
* @param the element type
* @param func the function that returns an iterable.
* @return the new iterable
*/
public static Iterable defer(
final Func0 extends Iterable> func) {
return new DeferIterable(func);
}
/**
* Convert the source materialized elements into normal iterator behavior.
* The returned iterator will throw an UnsupportedOperationException
for its remove()
method.
* The returned iterator will throw an UnsupportedOperationException
* for its remove()
method.
* @param the source element types
* @param source the source of T options
* @return the new iterable
*/
public static Iterable dematerialize(
final Iterable extends Notification extends T>> source) {
return new DematerializeIterable(source);
}
/**
* Returns an iterable which filters its elements based if they were ever seen before in
* the current iteration.
* Value equality is computed by reference equality and equals()
* @param the source element type
* @param source the source of Ts
* @return the new iterable
*/
public static Iterable distinct(
final Iterable extends T> source) {
return distinct(source, IxHelperFunctions.identity(), IxHelperFunctions.identity());
}
/**
* Returns an iterable which filters its elements by an unique key
* in a way that when multiple source items produce the same key, only
* the first one ever seen gets relayed further on.
* Key equality is computed by reference equality and equals()
* @param the source element type
* @param the key element type
* @param the output element type
* @param source the source of Ts
* @param keySelector the key selector for only-once filtering
* @param valueSelector the value select for the output of the first key cases
* @return the new iterable
*/
public static Iterable distinct(
final Iterable extends T> source,
final Func1 super T, ? extends U> keySelector,
final Func1 super T, ? extends V> valueSelector) {
return map(filterIndexed(source,
new Func0>() {
@Override
public Func2 call() {
return new Func2() {
final Set memory = new HashSet();
@Override
public Boolean call(Integer index, T param1) {
return memory.add(keySelector.call(param1));
}
};
}
})
, new Func1() {
@Override
public V call(T param1) {
return valueSelector.call(param1);
}
});
}
/**
* Creates an iterable which ensures that subsequent values of T are not equal (reference and equals).
* @param the element type
* @param source the source iterable
* @return the new iterable
*/
public static Iterable distinctNext(
final Iterable extends T> source) {
return filterIndexed(source,
new Func0>() {
@Override
public Func2 call() {
return new Func2() {
/** Is this the first element? */
boolean first = true;
/** The last seen element. */
T last;
@Override
public Boolean call(Integer index, T param1) {
if (first) {
first = false;
last = param1;
return true;
}
if (last == param1 || (last != null && last.equals(param1))) {
last = param1;
return false;
}
last = param1;
return true;
}
};
}
});
}
/**
* Creates an iterable which ensures that subsequent values of
* T are not equal in respect to the extracted keys (reference and equals).
* @param the element type
* @param the key type
* @param source the source iterable
* @param keyExtractor the function to extract the keys which will be compared
* @return the new iterable
*/
public static Iterable distinctNext(
final Iterable extends T> source,
final Func1 keyExtractor) {
return filterIndexed(source,
new Func0>() {
@Override
public Func2 call() {
return new Func2() {
/** Is this the first element? */
boolean first = true;
/** The last seen element. */
U last;
@Override
public Boolean call(Integer index, T param1) {
U key = keyExtractor.call(param1);
if (first) {
first = false;
last = key;
return true;
}
if (last == key || (last != null && last.equals(key))) {
last = key;
return false;
}
last = key;
return true;
}
};
}
});
}
/**
* Construct a new iterable which will invoke the specified action
* before the source value gets relayed through it.
* Can be used to inject side-effects before returning a value.
* The returned iterator forwards all remove()
calls
* to the source.
* @param the returned element type
* @param source the source iterable
* @param action the action to invoke before each next() is returned.
* @return the new iterable
*/
public static Iterable doOnNext(
final Iterable extends T> source,
final Action1 super T> action) {
return new DoOnNextIterable(source, action);
}
/**
* Returns an iterable which reiterates over and over again on source
* as long as the gate is true. The gate function is checked only
* when a pass over the source stream was completed.
* Note that using this operator on an empty iterable may result
* in a direct infinite loop in hasNext() or next() calls depending on the gate function.
* The returned iterator forwards all remove()
calls
* to the source.
* @param the source element type
* @param source the source of Ts
* @param gate the gate function to stop the repeat
* @return the new iterable
*/
public static Iterable doWhile(
final Iterable extends T> source,
final Func0 gate) {
return new DoWhileIterable(source, gate);
}
/**
* Determines whether two iterables contain equal elements in the same
* order. More specifically, this method returns {@code true} if
* {@code iterable1} and {@code iterable2} contain the same number of
* elements and every element of {@code iterable1} is equal to the
* corresponding element of {@code iterable2}.
* @param iterable1 the first iterable
* @param iterable2 the second iterable
* @return true if both iterables are either empty or contain the same number and equal items
*/
public static boolean elementsEqual(Iterable> iterable1,
Iterable> iterable2) {
Iterator> iterator1 = iterable1.iterator();
Iterator> iterator2 = iterable2.iterator();
return elementsEqual(iterator1, iterator2);
}
/**
* Compares two iterators wether they contain the same element in terms of numbers
* and nullsafe Object.equals().
* @param iterator1 the first iterator
* @param iterator2 the second interator
* @return true if they are equal
*/
public static boolean elementsEqual(
Iterator> iterator1,
Iterator> iterator2) {
try {
while (iterator1.hasNext()) {
if (!iterator2.hasNext()) {
return false;
}
Object o1 = iterator1.next();
Object o2 = iterator2.next();
if (!equal(o1, o2)) {
return false;
}
}
return !iterator2.hasNext();
} finally {
unsubscribe(iterator1);
unsubscribe(iterator2);
}
}
/**
* Returns an empty iterable which will not produce elements.
* Its hasNext()
returns always false,
* next()
throws a NoSuchElementException
* and remove()
throws an IllegalStateException
.
* Note that the Collections.emptyIterable()
static method is introduced by Java 7.
* @param the element type, irrelevant
* @return the iterable
*/
@SuppressWarnings("unchecked")
public static Iterable empty() {
return (Iterable)EMPTY_ITERABLE;
}
/**
* Creates an iterable sequence which returns all elements from source
* followed by the supplied value as last.
* The returned iterable forwards all {@code remove()}
* methods to the source iterable, except the last element where it
* throws UnsupportedOperationException.
* @param the element type
* @param source the source sequence
* @param value the value to append
* @return the new iterable
*/
public static Iterable endWith(
final Iterable extends T> source, T value) {
return concat(source, just(value));
}
/**
* Compare two object in a null-safe manner.
* @param a the first object
* @param b the second object
* @return true if both are null or equal according to Object.equals
*/
private static boolean equal(Object a, Object b) {
return (a == b) || ((a != null) && a.equals(b));
}
/**
* Creates an onError notification.
* @param the value type
* @param t the throwable
* @return the notification
*/
static Notification err(Throwable t) {
return Notification.createOnError(t);
}
/**
* Returns an iterable which executes the given action after
* the stream completes.
* The returned iterator forwards all remove()
calls
* to the source.
* @param the element type
* @param source the source of Ts
* @param action the action to invoke
* @return the new iterable
*/
public static Iterable doOnCompleted(
final Iterable extends T> source,
final Action0 action) {
return new DoOnCompletedIterable(action, source);
}
/**
* Returns the first element from the iterable sequence or
* throws a NoSuchElementException.
* @param the value type
* @param src the source sequence
* @return the first element
*/
public static T first(Iterable extends T> src) {
Iterator extends T> itor = src.iterator();
try {
return itor.next();
} finally {
unsubscribe(itor);
}
}
/**
* Returns the first element from the sequence or the default
* value if this sequence is empty
* @param the value type
* @param src the source sequence
* @param defaultValue the default value to return
* @return the first or default value
* @since 0.91.2
*/
public static T firstOrDefault(Iterable extends T> src, T defaultValue) {
Iterator extends T> itor = src.iterator();
try {
if (itor.hasNext()) {
return itor.next();
}
return defaultValue;
} finally {
Interactive.unsubscribe(itor);
}
}
/**
* Returns an iterable which runs the source iterable and
* returns elements from the iterable returned by the function call.
* The difference from flatMap is that the {@code Iterable<U>}s are
* created before their concatenation starts.
* @param the source element type
* @param the output element type
* @param source the source
* @param selector the result selector
* @return the new iterable
*/
public static Iterable flatMapAll(
final Iterable extends T> source,
final Func1 super T, ? extends Iterable extends U>> selector) {
return concat(map(source, selector));
}
/**
* Iterate over the source and submit each value to the
* given action. Basically, a for-each loop with pluggable
* action.
* This method is useful when the concrete values from the iterator
* are not needed but the iteration itself implies some side effects.
* @param the element type of the iterable
* @param source the iterable
* @param action the action to invoke on with element
*/
public static void forEach(
final Iterable extends T> source,
Action1 super T> action) {
Iterator extends T> iter = source.iterator();
try {
while (iter.hasNext()) {
T t = iter.next();
action.call(t);
}
} finally {
unsubscribe(iter);
}
}
/**
* A generator function which returns Ts based on the termination condition and the way it computes the next values.
* This is equivalent to:
*
* T value = seed;
* while (predicate(value)) {
* yield value;
* value = next(value);
* }
*
* The returned iterator will throw an UnsupportedOperationException
* for its remove()
method.
* @param the element type
* @param seed the initial value
* @param predicate the predicate to terminate the process
* @param next the function that computes the next value.
* @return the new iterable
*/
public static Iterable generate(
final T seed,
final Func1 super T, Boolean> predicate,
final Func1 super T, ? extends T> next) {
return new GenerateIterable(seed, next, predicate);
}
/**
* A generator function which returns Ts based on the termination condition and the way it computes the next values,
* but the first T to be returned is preceded by an initialDelay
amount of wait and each
* subsequent element is then generated after betweenDelay
sleep.
* The sleeping is blocking the current thread which invokes the hasNext()/next() methods.
* This is equivalent to:
*
* T value = seed;
* sleep(initialDelay);
* if (predicate(value)) {
* yield value;
* }
* value = next(value);
* sleep(betweenDelay);
* while (predicate(value)) {
* yield value;
* value = next(value);
* sleep(betweenDelay);
* }
*
* The returned iterator will throw an UnsupportedOperationException
* for its remove()
method.
* @param the element type
* @param seed the initial value
* @param predicate the predicate to terminate the process
* @param next the function that computes the next value.
* @param initialDelay the initial delay
* @param betweenDelay the between delay
* @param unit the time unit for initialDelay and betweenDelay
* @return the new iterable
*/
public static Iterable generate(
final T seed,
final Func1 super T, Boolean> predicate,
final Func1 super T, ? extends T> next,
final long initialDelay,
final long betweenDelay,
final TimeUnit unit) {
return new GenerateIterableTimed(predicate, next, seed, initialDelay,
betweenDelay, unit);
}
/**
* Creates an iterable which traverses the source iterable,
* and based on the key selector, groups values of T into GroupedIterables,
* which can be iterated over later on.
* The equivalence of the keys are determined via reference
* equality and equals()
equality.
* The returned iterator will throw an UnsupportedOperationException
* for its remove()
method.
* @param the source element type
* @param the result group keys
* @param source the source of Ts
* @param keySelector the key selector
* @return the new iterable
*/
public static Iterable> groupBy(
final Iterable extends T> source,
final Func1 super T, ? extends V> keySelector
) {
return groupBy(source, keySelector, IxHelperFunctions.identity());
}
/**
* Creates an iterable which traverses the source iterable,
* and based on the key selector, groups values extracted by valueSelector into GroupedIterables,
* which can be iterated over later on.
* The equivalence of the keys are determined via reference
* equality and equals()
equality.
* The returned iterator will throw an UnsupportedOperationException
* for its remove()
method.
* @param the source element type
* @param the result group element type
* @param the result group keys
* @param source the source of Ts
* @param keySelector the key selector
* @param valueSelector the value selector
* @return the new iterable
*/
public static Iterable> groupBy(
final Iterable extends T> source,
final Func1 super T, ? extends V> keySelector,
final Func1 super T, ? extends U> valueSelector) {
return distinct(new Iterable>() {
@Override
public Iterator> iterator() {
final Map> groups = new LinkedHashMap>();
final Iterator extends T> it = source.iterator();
return new Iterator>() {
Iterator> groupIt;
@Override
public boolean hasNext() {
return it.hasNext() || (groupIt != null && groupIt.hasNext());
}
@Override
public GroupedIterable next() {
if (hasNext()) {
if (groupIt == null) {
try {
while (it.hasNext()) {
T t = it.next();
V v = keySelector.call(t);
U u = valueSelector.call(t);
GroupedIterable g = groups.get(v);
if (g == null) {
g = new GroupedIterable(v);
groups.put(v, g);
}
g.add(u);
}
} finally {
unsubscribe(it);
}
groupIt = groups.values().iterator();
}
return groupIt.next();
}
throw new NoSuchElementException();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
}, new Func1, V>() {
@Override
public V call(GroupedIterable param1) {
return param1.getKey();
}
}, IxHelperFunctions.>identity());
}
/**
* Returns an iterable which invokes the given next
* action for each element and the finish
action when
* the source completes.
* @param the source element type
* @param source the source of Ts
* @param next the action to invoke on each element
* @param finish the action to invoke after the last element
* @return the new iterable
*/
public static Iterable doOnEach(
final Iterable extends T> source,
Action1 super T> next,
Action0 finish) {
return doOnEach(source, next, Actions.empty(), finish);
}
/**
* Returns an iterable which invokes the given next
* action for each element and error
when an exception is thrown.
* @param the source element type
* @param source the source of Ts
* @param next the action to invoke on each element
* @param error the error action to invoke for an error
* @return the new iterable
*/
public static Iterable doOnEach(
final Iterable extends T> source,
final Action1 super T> next,
final Action1 super Throwable> error) {
return doOnEach(source, next, error, Actions.empty());
}
/**
* Returns an iterable which invokes the given next
* action for each element and the finish
action when
* the source completes and error
when an exception is thrown.
* The returned iterator forwards all remove()
calls
* to the source.
* @param the source element type
* @param source the source of Ts
* @param next the action to invoke on each element
* @param error the error action to invoke for an error
* @param finish the action to invoke after the last element
* @return the new iterable
*/
public static Iterable doOnEach(
final Iterable extends T> source,
final Action1 super T> next,
final Action1 super Throwable> error,
final Action0 finish) {
return new DoOnEachIterable(error, finish, source);
}
/**
* Returns a single true if the target iterable is empty.
* @param source source iterable with any type
* @return the new iterable
*/
public static Iterable isEmpty(
final Iterable> source) {
return map(any(source), IxHelperFunctions.negate());
}
/**
* Concatenates the source strings one after another and uses the given separator.
* The returned iterator forwards all remove()
calls
* to the source.
* @param source the source
* @param separator the separator to use
* @return the new iterable
*/
public static Iterable join(
final Iterable> source,
final String separator) {
return aggregate(source,
new Func2() {
@Override
public StringBuilder call(StringBuilder param1, Object param2) {
if (param1 == null) {
param1 = new StringBuilder();
} else {
param1.append(separator);
}
param1.append(param2);
return param1;
}
},
new Func2() {
@Override
public String call(StringBuilder param1, Integer param2) {
return param1.toString();
}
}
);
}
/**
* Returns the last element of the iterable or throws a NoSuchElementException
if the iterable is empty.
* @param the source element type
* @param source the source of Ts
* @return the last value
*/
public static T last(
final Iterable extends T> source) {
Iterator extends T> it = source.iterator();
try {
if (it.hasNext()) {
T t;
do {
t = it.next();
} while (it.hasNext());
return t;
}
} finally {
unsubscribe(it);
}
throw new NoSuchElementException();
}
/**
* Returns the last element of this sequence or the default value if
* this sequence is empty.
* @param the source element type
* @param source the source of Ts
* @param defaultValue the default value to return if this sequence is empty
* @return the last value
*/
public static T lastOrDefault(
final Iterable extends T> source, T defaultValue) {
Iterator extends T> it = source.iterator();
try {
if (it.hasNext()) {
T t;
do {
t = it.next();
} while (it.hasNext());
return t;
}
return defaultValue;
} finally {
unsubscribe(it);
}
}
/**
* Creates an iterable which is a transforms the source
* elements by using the selector function.
* The function receives the current index and the current element.
* @param the source element type
* @param the output element type
* @param source the source iterable
* @param selector the selector function
* @return the new iterable
*/
public static Iterable map(
final Iterable extends T> source,
final Func1 super T, ? extends U> selector) {
return mapIndexed(source, new Func2() {
@Override
public U call(Integer param1, T param2) {
return selector.call(param2);
}
});
}
/**
* Creates an iterable which is a transforms the source
* elements by using the selector function.
* The function receives the current index and the current element.
* The returned iterator forwards all remove()
calls
* to the source.
* @param the source element type
* @param the output element type
* @param source the source iterable
* @param selector the selector function
* @return the new iterable
*/
public static Iterable mapIndexed(
final Iterable extends T> source,
final Func2 super Integer, ? super T, ? extends U> selector) {
return new MapIndexedIterable(source, selector);
}
/**
* Transforms the sequence of the source iterable into an option sequence of
* Notification.some(), Notification.none() and Notification.error() values, depending on
* what the source's hasNext() and next() produces.
* The returned iterator will throw an UnsupportedOperationException
for its remove()
method.
* The returned iterator will throw an UnsupportedOperationException
* for its remove()
method.
* @param the source element type
* @param source the source of at least Ts.
* @return the new iterable
*/
public static Iterable> materialize(
final Iterable extends T> source) {
return new MaterializeIterable(source);
}
/**
* Returns the maximum value of the given iterable source.
* @param the element type, which must be self comparable
* @param source the source elements
* @return the new iterable
*/
public static > Iterable max(
final Iterable extends T> source) {
return aggregate(source, IxHelperFunctions.max(),
IxHelperFunctions.identityFirst());
}
/**
* Returns the maximum value of the given iterable source in respect to the supplied comparator.
* @param the element type, which must be self comparable
* @param source the source elements
* @param comparator the comparator to use
* @return the new iterable
*/
public static Iterable max(
final Iterable extends T> source,
final Comparator super T> comparator) {
return aggregate(source, IxHelperFunctions.max(comparator), IxHelperFunctions.identityFirst());
}
/**
* Returns an iterator which will produce a single List of the maximum values encountered
* in the source stream based on the supplied key selector.
* @param the source element type, which must be self comparable
* @param source the source of Ts
* @return the new iterable
*/
public static > Iterable> maxBy(
final Iterable extends T> source) {
return minMax(source, IxHelperFunctions.identity(), IxHelperFunctions.comparator(), true);
}
/**
* Returns an iterator which will produce a single List of the maximum values encountered
* in the source stream based on the supplied comparator.
* @param the source element type
* @param source the source of Ts
* @param comparator the key comparator
* @return the new iterable
*/
public static Iterable> maxBy(
final Iterable extends T> source,
final Comparator super T> comparator) {
return minMax(source, IxHelperFunctions.identity(), comparator, true);
}
/**
* Returns an iterator which will produce a single List of the maximum values encountered
* in the source stream based on the supplied key selector.
* @param the source element type
* @param the key type, which must be self-comparable
* @param source the source of Ts
* @param keySelector the selector for keys
* @return the new iterable
*/
public static > Iterable> maxBy(
final Iterable extends T> source,
final Func1 super T, ? extends U> keySelector) {
return minMax(source, keySelector, IxHelperFunctions.comparator(), true);
}
/**
* Returns an iterator which will produce a single List of the minimum values encountered
* in the source stream based on the supplied key selector and comparator.
* @param the source element type
* @param the key type
* @param source the source of Ts
* @param keySelector the selector for keys
* @param keyComparator the key comparator
* @return the new iterable
*/
public static Iterable> maxBy(
final Iterable extends T> source,
final Func1 super T, ? extends U> keySelector,
final Comparator super U> keyComparator) {
return minMax(source, keySelector, keyComparator, true);
}
/**
* Enumerates the source iterable once and caches its results.
* Any iterator party will basically drain this cache, e.g.,
* reiterating over this iterable will produce no results.
* Note: the name is not a misspelling, see