com.landawn.abacus.util.stream.CharStream Maven / Gradle / Ivy
Show all versions of abacus-common Show documentation
/*
* Copyright (C) 2016, 2017, 2018, 2019 HaiYang Li
*
* 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 com.landawn.abacus.util.stream;
import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.nio.CharBuffer;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Queue;
import java.util.Random;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.BinaryOperator;
import java.util.function.BooleanSupplier;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
import com.landawn.abacus.annotation.Beta;
import com.landawn.abacus.annotation.IntermediateOp;
import com.landawn.abacus.annotation.LazyEvaluation;
import com.landawn.abacus.annotation.ParallelSupported;
import com.landawn.abacus.annotation.SequentialOnly;
import com.landawn.abacus.annotation.TerminalOp;
import com.landawn.abacus.exception.UncheckedIOException;
import com.landawn.abacus.util.Array;
import com.landawn.abacus.util.AsyncExecutor;
import com.landawn.abacus.util.CharIterator;
import com.landawn.abacus.util.CharList;
import com.landawn.abacus.util.CharSummaryStatistics;
import com.landawn.abacus.util.ClassUtil;
import com.landawn.abacus.util.ContinuableFuture;
import com.landawn.abacus.util.Fn;
import com.landawn.abacus.util.Fn.FnC;
import com.landawn.abacus.util.Holder;
import com.landawn.abacus.util.IOUtil;
import com.landawn.abacus.util.IndexedChar;
import com.landawn.abacus.util.MergeResult;
import com.landawn.abacus.util.MutableInt;
import com.landawn.abacus.util.N;
import com.landawn.abacus.util.Pair;
import com.landawn.abacus.util.Percentage;
import com.landawn.abacus.util.RateLimiter;
import com.landawn.abacus.util.Throwables;
import com.landawn.abacus.util.u.Optional;
import com.landawn.abacus.util.u.OptionalChar;
import com.landawn.abacus.util.u.OptionalDouble;
import com.landawn.abacus.util.function.CharBiFunction;
import com.landawn.abacus.util.function.CharBiPredicate;
import com.landawn.abacus.util.function.CharBinaryOperator;
import com.landawn.abacus.util.function.CharConsumer;
import com.landawn.abacus.util.function.CharFunction;
import com.landawn.abacus.util.function.CharNFunction;
import com.landawn.abacus.util.function.CharPredicate;
import com.landawn.abacus.util.function.CharSupplier;
import com.landawn.abacus.util.function.CharTernaryOperator;
import com.landawn.abacus.util.function.CharToIntFunction;
import com.landawn.abacus.util.function.CharTriPredicate;
import com.landawn.abacus.util.function.CharUnaryOperator;
import com.landawn.abacus.util.function.ObjCharConsumer;
import com.landawn.abacus.util.function.ToCharFunction;
/**
* The Stream will be automatically closed after execution(A terminal method is executed/triggered).
*
*
* @see BaseStream
* @see Stream
*/
@com.landawn.abacus.annotation.Immutable
@LazyEvaluation
public abstract class CharStream
extends StreamBase {
static final Random RAND = new SecureRandom();
CharStream(final boolean sorted, final Collection closeHandlers) {
super(sorted, null, closeHandlers);
}
@Override
public CharStream rateLimited(final RateLimiter rateLimiter) {
checkArgNotNull(rateLimiter, "rateLimiter");
final CharConsumer action = it -> rateLimiter.acquire();
if (isParallel()) {
return sequential().onEach(action).parallel(maxThreadNum(), executorNumForVirtualThread(), splitor(), asyncExecutor(), cancelUncompletedThreads());
} else {
return onEach(action);
}
}
@Override
public CharStream skipUntil(final CharPredicate predicate) {
assertNotClosed();
return dropWhile(t -> !predicate.test(t));
}
/**
* Returns a stream consisting of the results of applying the given
* function to the elements of this stream.
*
* This is an intermediate
* operation.
*
* @param mapper a non-interfering,
* stateless
* function to apply to each element
* @return
*/
@ParallelSupported
@IntermediateOp
public abstract CharStream map(CharUnaryOperator mapper);
/**
* Returns a {@code IntStream} consisting of the results of applying the
* given function to the elements of this stream.
*
*
This is an intermediate
* operation.
*
* @param mapper a non-interfering,
* stateless
* function to apply to each element
* @return
*/
@ParallelSupported
@IntermediateOp
public abstract IntStream mapToInt(CharToIntFunction mapper);
/**
* Returns an object-valued {@code Stream} consisting of the results of
* applying the given function to the elements of this stream.
*
*
This is an
* intermediate operation.
*
* @param the element type of the new stream
* @param mapper a non-interfering,
* stateless
* function to apply to each element
* @return
*/
@ParallelSupported
@IntermediateOp
public abstract Stream mapToObj(CharFunction extends U> mapper);
/**
* Returns a stream consisting of the results of replacing each element of
* this stream with the contents of a mapped stream produced by applying
* the provided mapping function to each element.
*
*
This is an intermediate
* operation.
*
* @param mapper a non-interfering,
* stateless
* function to apply to each element which produces an
* {@code CharStream} of new values
* @return
* @see Stream#flatMap(Function)
*/
@ParallelSupported
@IntermediateOp
public abstract CharStream flatMap(CharFunction extends CharStream> mapper);
// public abstract CharStream flatmap(CharFunction mapper);
@ParallelSupported
@IntermediateOp
public abstract CharStream flatmap(CharFunction mapper);
@ParallelSupported
@IntermediateOp
public abstract IntStream flatMapToInt(CharFunction extends IntStream> mapper);
@ParallelSupported
@IntermediateOp
public abstract Stream flatMapToObj(CharFunction extends Stream> mapper);
@ParallelSupported
@IntermediateOp
public abstract Stream flatmapToObj(CharFunction extends Collection> mapper);
@ParallelSupported
@IntermediateOp
public abstract Stream flattMapToObj(CharFunction mapper);
/**
* Note: copied from StreamEx: https://github.com/amaembo/streamex
*
* @param
* @param mapper
* @return
*/
@Beta
@ParallelSupported
@IntermediateOp
public abstract CharStream mapPartial(CharFunction mapper);
/**
* Note: copied from StreamEx: https://github.com/amaembo/streamex
*
*
*
* Returns a stream consisting of results of applying the given function to
* the ranges created from the source elements.
* This is a quasi-intermediate
* partial reduction operation.
*
* @param sameRange a non-interfering, stateless predicate to apply to
* the leftmost and next elements which returns true for elements
* which belong to the same range.
* @param mapper a non-interfering, stateless function to apply to the
* range borders and produce the resulting element. If value was
* not merged to the interval, then mapper will receive the same
* value twice, otherwise it will receive the leftmost and the
* rightmost values which were merged to the range.
* @return
* @see #collapse(CharBiPredicate, CharBinaryOperator)
* @see Stream#rangeMap(BiPredicate, BiFunction)
*/
@SequentialOnly
@IntermediateOp
public abstract CharStream rangeMap(final CharBiPredicate sameRange, final CharBinaryOperator mapper);
/**
* Note: copied from StreamEx: https://github.com/amaembo/streamex
*
*
*
* Returns a stream consisting of results of applying the given function to
* the ranges created from the source elements.
* This is a quasi-intermediate
* partial reduction operation.
*
* @param sameRange a non-interfering, stateless predicate to apply to
* the leftmost and next elements which returns true for elements
* which belong to the same range.
* @param mapper a non-interfering, stateless function to apply to the
* range borders and produce the resulting element. If value was
* not merged to the interval, then mapper will receive the same
* value twice, otherwise it will receive the leftmost and the
* rightmost values which were merged to the range.
* @return
* @see Stream#rangeMap(BiPredicate, BiFunction)
*/
@SequentialOnly
@IntermediateOp
public abstract Stream rangeMapToObj(final CharBiPredicate sameRange, final CharBiFunction mapper);
/**
* Merge series of adjacent elements which satisfy the given predicate using
* the merger function and return a new stream.
*
*
* This method only runs sequentially, even in parallel stream.
*
* @param collapsible
* @return
*/
@SequentialOnly
@IntermediateOp
public abstract Stream collapse(final CharBiPredicate collapsible);
/**
* Merge series of adjacent elements which satisfy the given predicate using
* the merger function and return a new stream.
*
*
* This method only runs sequentially, even in parallel stream.
*
* @param collapsible
* @param mergeFunction
* @return
*/
@SequentialOnly
@IntermediateOp
public abstract CharStream collapse(final CharBiPredicate collapsible, final CharBinaryOperator mergeFunction);
/**
*
* @param collapsible test the current element with the first element and previous element in the series. The first parameter is the first element of this series, the second parameter is the previous element and the third parameter is the current element.
* @param mergeFunction
* @return
*/
@SequentialOnly
@IntermediateOp
public abstract CharStream collapse(final CharTriPredicate collapsible, final CharBinaryOperator mergeFunction);
/**
* Returns a {@code Stream} produced by iterative application of a accumulation function
* to an initial element {@code init} and next element of the current stream.
* Produces a {@code Stream} consisting of {@code init}, {@code acc(init, value1)},
* {@code acc(acc(init, value1), value2)}, etc.
*
* This is an intermediate operation.
*
*
Example:
*
* accumulator: (a, b) -> a + b
* stream: [1, 2, 3, 4, 5]
* result: [1, 3, 6, 10, 15]
*
*
*
* This method only runs sequentially, even in parallel stream.
*
* @param accumulator the accumulation function
* @return
*/
@SequentialOnly
@IntermediateOp
public abstract CharStream scan(final CharBinaryOperator accumulator);
/**
* Returns a {@code Stream} produced by iterative application of a accumulation function
* to an initial element {@code init} and next element of the current stream.
* Produces a {@code Stream} consisting of {@code init}, {@code acc(init, value1)},
* {@code acc(acc(init, value1), value2)}, etc.
*
* This is an intermediate operation.
*
*
Example:
*
* init:10
* accumulator: (a, b) -> a + b
* stream: [1, 2, 3, 4, 5]
* result: [11, 13, 16, 20, 25]
*
*
*
* This method only runs sequentially, even in parallel stream.
*
* @param init the initial value. it's only used once by accumulator
to calculate the fist element in the returned stream.
* It will be ignored if this stream is empty and won't be the first element of the returned stream.
*
* @param accumulator the accumulation function
* @return
*/
@SequentialOnly
@IntermediateOp
public abstract CharStream scan(final char init, final CharBinaryOperator accumulator);
/**
*
* @param init
* @param accumulator
* @param initIncluded
* @return
*/
@SequentialOnly
@IntermediateOp
public abstract CharStream scan(final char init, final CharBinaryOperator accumulator, final boolean initIncluded);
@SequentialOnly
@IntermediateOp
public abstract CharStream prepend(final char... a);
@SequentialOnly
@IntermediateOp
public abstract CharStream append(final char... a);
@SequentialOnly
@IntermediateOp
public abstract CharStream appendIfEmpty(final char... a);
@SequentialOnly
@TerminalOp
public abstract CharList toCharList();
/**
*
* @param keyMapper
* @param valueMapper
* @return
* @see Collectors#toMap(Throwables.Function, Throwables.Function)
*/
@ParallelSupported
@TerminalOp
public abstract Map toMap(Throwables.CharFunction extends K, E> keyMapper,
Throwables.CharFunction extends V, E2> valueMapper) throws E, E2;
/**
*
* @param keyMapper
* @param valueMapper
* @param mapFactory
* @return
* @see Collectors#toMap(Throwables.Function, Throwables.Function, Supplier)
*/
@ParallelSupported
@TerminalOp
public abstract , E extends Exception, E2 extends Exception> M toMap(Throwables.CharFunction extends K, E> keyMapper,
Throwables.CharFunction extends V, E2> valueMapper, Supplier extends M> mapFactory) throws E, E2;
/**
*
* @param keyMapper
* @param valueMapper
* @param mergeFunction
* @return
* @see Collectors#toMap(Throwables.Function, Throwables.Function, BinaryOperator)
*/
@ParallelSupported
@TerminalOp
public abstract Map toMap(Throwables.CharFunction extends K, E> keyMapper,
Throwables.CharFunction extends V, E2> valueMapper, BinaryOperator mergeFunction) throws E, E2;
/**
*
* @param keyMapper
* @param valueMapper
* @param mergeFunction
* @param mapFactory
* @return
* @see Collectors#toMap(Throwables.Function, Throwables.Function, BinaryOperator, Supplier)
*/
@ParallelSupported
@TerminalOp
public abstract , E extends Exception, E2 extends Exception> M toMap(Throwables.CharFunction extends K, E> keyMapper,
Throwables.CharFunction extends V, E2> valueMapper, BinaryOperator mergeFunction, Supplier extends M> mapFactory) throws E, E2;
/**
*
* @param keyMapper
* @param downstream
* @return
* @see Collectors#groupingBy(Throwables.Function, Collector)
*/
@ParallelSupported
@TerminalOp
public abstract Map groupTo(Throwables.CharFunction extends K, E> keyMapper,
final Collector downstream) throws E;
/**
*
* @param keyMapper
* @param downstream
* @param mapFactory
* @return
* @see Collectors#groupingBy(Throwables.Function, Collector, Supplier)
*/
@ParallelSupported
@TerminalOp
public abstract , E extends Exception> M groupTo(Throwables.CharFunction extends K, E> keyMapper,
final Collector downstream, final Supplier extends M> mapFactory) throws E;
/**
*
* @param identity
* @param op
* @return
*/
@ParallelSupported
@TerminalOp
public abstract char reduce(char identity, CharBinaryOperator op);
/**
*
* @param op
* @return
*/
@ParallelSupported
@TerminalOp
public abstract OptionalChar reduce(CharBinaryOperator op);
/**
*
* @param
* @param supplier
* @param accumulator
* @param combiner
* @return
* @see Stream#collect(Supplier, BiConsumer, BiConsumer)
*/
@ParallelSupported
@TerminalOp
public abstract R collect(Supplier supplier, ObjCharConsumer super R> accumulator, BiConsumer combiner);
/**
* Only call this method when the returned type {@code R} is one types: {@code Collection/Map/StringBuilder/Multiset/LongMultiset/Multimap/BooleanList/IntList/.../DoubleList}.
* Otherwise, please call {@link #collect(Supplier, ObjCharConsumer, BiConsumer)}.
*
* @param supplier
* @param accumulator
* @return
* @see #collect(Supplier, ObjCharConsumer, BiConsumer)
* @see Stream#collect(Supplier, BiConsumer)
* @see Stream#collect(Supplier, BiConsumer, BiConsumer)
*/
@ParallelSupported
@TerminalOp
public abstract R collect(Supplier supplier, ObjCharConsumer super R> accumulator);
@ParallelSupported
@TerminalOp
public abstract void forEach(final Throwables.CharConsumer action) throws E;
@ParallelSupported
@TerminalOp
public abstract void forEachIndexed(Throwables.IndexedCharConsumer action) throws E;
@ParallelSupported
@TerminalOp
public abstract boolean anyMatch(final Throwables.CharPredicate predicate) throws E;
@ParallelSupported
@TerminalOp
public abstract boolean allMatch(final Throwables.CharPredicate predicate) throws E;
@ParallelSupported
@TerminalOp
public abstract boolean noneMatch(final Throwables.CharPredicate predicate) throws E;
@ParallelSupported
@TerminalOp
public abstract OptionalChar findFirst(final Throwables.CharPredicate predicate) throws E;
/**
* Returns the first element matched by {@code predicateForFirst} if found or the first element if this stream is not empty
* Otherwise an empty {@code OptionalChar} will be returned.
*
* @param
* @param predicateForFirst
* @return
* @throws E
*/
@ParallelSupported
@TerminalOp
public abstract OptionalChar findFirstOrAny(Throwables.CharPredicate predicateForFirst) throws E;
/**
* Returns the first element matched by {@code predicateForFirst} if found or the last element if this stream is not empty
* Otherwise an empty {@code OptionalChar} will be returned.
*
* @param
* @param predicateForFirst
* @return
* @throws E
*/
@ParallelSupported
@TerminalOp
public abstract OptionalChar findFirstOrLast(Throwables.CharPredicate predicateForFirst) throws E;
/**
* Consider using: {@code stream.reversed().findFirst(predicate)} for better performance if possible.
*
* @param
* @param predicate
* @return
* @throws E
*/
@ParallelSupported
@TerminalOp
public abstract OptionalChar findLast(final Throwables.CharPredicate predicate) throws E;
@ParallelSupported
@TerminalOp
public abstract OptionalChar findAny(final Throwables.CharPredicate predicate) throws E;
/**
* Returns an {@code OptionalChar} describing the minimum element of this
* stream, or an empty optional if this stream is empty. This is a special
* case of a reduction
* and is equivalent to:
* {@code
* return reduce(Chareger::min);
* }
*
* This is a terminal operation.
*
* @return an {@code OptionalChar} containing the minimum element of this
* stream, or an empty {@code OptionalChar} if the stream is empty
*/
@SequentialOnly
@TerminalOp
public abstract OptionalChar min();
/**
* Returns an {@code OptionalChar} describing the maximum element of this
* stream, or an empty optional if this stream is empty. This is a special
* case of a reduction
* and is equivalent to:
*
{@code
* return reduce(Chareger::max);
* }
*
* This is a terminal
* operation.
*
* @return an {@code OptionalChar} containing the maximum element of this
* stream, or an empty {@code OptionalChar} if the stream is empty
*/
@SequentialOnly
@TerminalOp
public abstract OptionalChar max();
/**
*
* @param k
* @return OptionalByte.empty() if there is no element or count less than k, otherwise the kth largest element.
*/
@SequentialOnly
@TerminalOp
public abstract OptionalChar kthLargest(int k);
@SequentialOnly
@TerminalOp
public abstract long sum();
@SequentialOnly
@TerminalOp
public abstract OptionalDouble average();
@SequentialOnly
@TerminalOp
public abstract CharSummaryStatistics summarize();
@SequentialOnly
@TerminalOp
public abstract Pair>> summarizeAndPercentiles();
/**
*
* @param b
* @param nextSelector first parameter is selected if Nth.FIRST
is returned, otherwise the second parameter is selected.
* @return
* @deprecated replaced by {@code mergeWith(CharStream, CharBiFunction)}
* @see #mergeWith(CharStream, CharBiFunction)
*/
@SequentialOnly
@IntermediateOp
@Deprecated
public CharStream merge(final CharStream b, final CharBiFunction nextSelector) {
return mergeWith(b, nextSelector);
}
/**
*
* @param b
* @param nextSelector first parameter is selected if Nth.FIRST
is returned, otherwise the second parameter is selected.
* @return
*/
@SequentialOnly
@IntermediateOp
public abstract CharStream mergeWith(final CharStream b, final CharBiFunction nextSelector);
@ParallelSupported
@IntermediateOp
public abstract CharStream zipWith(CharStream b, CharBinaryOperator zipFunction);
@ParallelSupported
@IntermediateOp
public abstract CharStream zipWith(CharStream b, CharStream c, CharTernaryOperator zipFunction);
@ParallelSupported
@IntermediateOp
public abstract CharStream zipWith(CharStream b, char valueForNoneA, char valueForNoneB, CharBinaryOperator zipFunction);
@ParallelSupported
@IntermediateOp
public abstract CharStream zipWith(CharStream b, CharStream c, char valueForNoneA, char valueForNoneB, char valueForNoneC, CharTernaryOperator zipFunction);
/**
* Returns a {@code LongStream} consisting of the elements of this stream,
* converted to {@code long}.
*
* This is an intermediate
* operation.
*
* @return a {@code LongStream} consisting of the elements of this stream,
* converted to {@code long}
*/
@SequentialOnly
@IntermediateOp
public abstract IntStream asIntStream();
/**
* Returns a {@code Stream} consisting of the elements of this stream,
* each boxed to an {@code Chareger}.
*
*
This is an intermediate
* operation.
*
* @return a {@code Stream} consistent of the elements of this stream,
* each boxed to an {@code Chareger}
*/
@SequentialOnly
@IntermediateOp
public abstract Stream boxed();
/**
* Remember to close this Stream after the iteration is done, if needed.
*
* @return
*/
@Override
public CharIterator iterator() {
assertNotClosed();
if (!isEmptyCloseHandlers(closeHandlers) && logger.isWarnEnabled()) {
logger.warn("### Remember to close " + ClassUtil.getSimpleClassName(getClass()));
}
return iteratorEx();
}
abstract CharIteratorEx iteratorEx();
public static CharStream empty() {
return new ArrayCharStream(N.EMPTY_CHAR_ARRAY, true, null);
}
public static CharStream ofNullable(final Character e) {
return e == null ? empty() : of(e);
}
@SafeVarargs
public static CharStream of(final char... a) {
return N.isNullOrEmpty(a) ? empty() : new ArrayCharStream(a);
}
public static CharStream of(final char[] a, final int startIndex, final int endIndex) {
return N.isNullOrEmpty(a) && (startIndex == 0 && endIndex == 0) ? empty() : new ArrayCharStream(a, startIndex, endIndex);
}
/**
* Takes the chars in the specified String as the elements of the Stream
*
* @param str
* @return
*/
public static CharStream of(final CharSequence str) {
return N.isNullOrEmpty(str) ? empty() : of(str, 0, str.length());
}
/**
* Takes the chars in the specified String as the elements of the Stream
*
* @param str
* @param startIndex
* @param endIndex
* @return
*/
public static CharStream of(final CharSequence str, final int startIndex, final int endIndex) {
N.checkFromToIndex(startIndex, endIndex, N.len(str));
if (N.isNullOrEmpty(str)) {
return empty();
}
// if (str instanceof String && IOUtil.JAVA_VERSION.atMost(JavaVersion.JAVA_1_8)) {
// return of(com.landawn.abacus.util.InternalUtil.getCharsForReadOnly((String) str), startIndex, endIndex);
// }
final CharIteratorEx iter = new CharIteratorEx() {
private int cursor = startIndex;
@Override
public boolean hasNext() {
return cursor < endIndex;
}
@Override
public char nextChar() {
return str.charAt(cursor++);
}
@Override
public long count() {
return endIndex - cursor;
}
};
return new IteratorCharStream(iter);
}
public static CharStream of(final Character[] a) {
return Stream.of(a).mapToChar(FnC.unbox());
}
public static CharStream of(final Character[] a, final int startIndex, final int endIndex) {
return Stream.of(a, startIndex, endIndex).mapToChar(FnC.unbox());
}
public static CharStream of(final Collection c) {
return Stream.of(c).mapToChar(FnC.unbox());
}
public static CharStream of(final CharIterator iterator) {
return iterator == null ? empty() : new IteratorCharStream(iterator);
}
public static CharStream of(final CharBuffer buf) {
if (buf == null) {
return empty();
}
return IntStream.range(buf.position(), buf.limit()).mapToChar(buf::get);
}
public static CharStream of(final File file) {
return of(IOUtil.newFileReader(file), true);
}
public static CharStream of(final Reader reader) {
return of(reader, false);
}
public static CharStream of(final Reader reader, final boolean closeReaderAferExecution) {
if (reader == null) {
return empty();
}
final CharIterator iter = new CharIterator() {
private final char[] buf = new char[8192];
private boolean isEnd = false;
private int count = 0;
private int idx = 0;
@Override
public boolean hasNext() {
if (idx >= count && !isEnd) {
try {
count = reader.read(buf);
idx = 0;
isEnd = count <= 0;
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
return count > idx;
}
@Override
public char nextChar() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return buf[idx++];
}
};
if (closeReaderAferExecution) {
return of(iter).onClose(Fn.close(reader));
} else {
return of(iter);
}
}
/**
* Lazy evaluation.
*
*
* This is equal to: {@code Stream.just(supplier).flatMapToChar(it -> it.get())}.
*
* @param
* @param supplier
* @return
*/
public static CharStream defer(final Supplier supplier) {
N.checkArgNotNull(supplier, "supplier");
return Stream.just(supplier).flatMapToChar(Supplier::get);
}
/**
* Lazy evaluation.
*
*
* This is equal to: {@code Stream.just(supplier).flatMapToChar(it -> it.get().stream())}.
*
* @param supplier
* @return
*/
@Beta
public static CharStream from(final Supplier supplier) {
N.checkArgNotNull(supplier, "supplier");
return Stream.just(supplier).flatMapToChar(it -> it.get().stream());
}
private static final Function flatMapper = CharStream::of;
private static final Function flattMapper = CharStream::flatten;
public static CharStream flatten(final char[][] a) {
return N.isNullOrEmpty(a) ? empty() : Stream.of(a).flatMapToChar(flatMapper);
}
public static CharStream flatten(final char[][] a, final boolean vertically) {
if (N.isNullOrEmpty(a)) {
return empty();
} else if (a.length == 1) {
return of(a[0]);
} else if (!vertically) {
return Stream.of(a).flatMapToChar(flatMapper);
}
long n = 0;
for (char[] e : a) {
n += N.len(e);
}
if (n == 0) {
return empty();
}
final int rows = N.len(a);
final long count = n;
final CharIterator iter = new CharIteratorEx() {
private int rowNum = 0;
private int colNum = 0;
private long cnt = 0;
@Override
public boolean hasNext() {
return cnt < count;
}
@Override
public char nextChar() {
if (cnt++ >= count) {
throw new NoSuchElementException();
}
if (rowNum == rows) {
rowNum = 0;
colNum++;
}
while (a[rowNum] == null || colNum >= a[rowNum].length) {
if (rowNum < rows - 1) {
rowNum++;
} else {
rowNum = 0;
colNum++;
}
}
return a[rowNum++][colNum];
}
};
return of(iter);
}
public static CharStream flatten(final char[][] a, final char valueForNone, final boolean vertically) {
if (N.isNullOrEmpty(a)) {
return empty();
} else if (a.length == 1) {
return of(a[0]);
}
long n = 0;
int maxLen = 0;
for (char[] e : a) {
n += N.len(e);
maxLen = N.max(maxLen, N.len(e));
}
if (n == 0) {
return empty();
}
final int rows = N.len(a);
final int cols = maxLen;
final long count = (long) rows * cols;
CharIterator iter = null;
if (vertically) {
iter = new CharIteratorEx() {
private int rowNum = 0;
private int colNum = 0;
private long cnt = 0;
@Override
public boolean hasNext() {
return cnt < count;
}
@Override
public char nextChar() {
if (cnt++ >= count) {
throw new NoSuchElementException();
}
if (rowNum == rows) {
rowNum = 0;
colNum++;
}
if (a[rowNum] == null || colNum >= a[rowNum].length) {
rowNum++;
return valueForNone;
} else {
return a[rowNum++][colNum];
}
}
};
} else {
iter = new CharIteratorEx() {
private int rowNum = 0;
private int colNum = 0;
private long cnt = 0;
@Override
public boolean hasNext() {
return cnt < count;
}
@Override
public char nextChar() {
if (cnt++ >= count) {
throw new NoSuchElementException();
}
if (colNum >= cols) {
colNum = 0;
rowNum++;
}
if (a[rowNum] == null || colNum >= a[rowNum].length) {
colNum++;
return valueForNone;
} else {
return a[rowNum][colNum++];
}
}
};
}
return of(iter);
}
public static CharStream flatten(final char[][][] a) {
return N.isNullOrEmpty(a) ? empty() : Stream.of(a).flatMapToChar(flattMapper);
}
public static CharStream range(final char startInclusive, final char endExclusive) {
if (startInclusive >= endExclusive) {
return empty();
}
return new IteratorCharStream(new CharIteratorEx() {
private char next = startInclusive;
private int cnt = endExclusive * 1 - startInclusive;
@Override
public boolean hasNext() {
return cnt > 0;
}
@Override
public char nextChar() {
if (cnt-- <= 0) {
throw new NoSuchElementException();
}
return next++;
}
@Override
public void advance(long n) {
cnt = n >= cnt ? 0 : cnt - (int) n;
next += n;
}
@Override
public long count() {
return cnt;
}
@Override
public char[] toArray() {
final char[] result = new char[cnt];
for (int i = 0; i < cnt; i++) {
result[i] = next++;
}
cnt = 0;
return result;
}
});
}
public static CharStream range(final char startInclusive, final char endExclusive, final int by) {
if (by == 0) {
throw new IllegalArgumentException("'by' can't be zero");
}
if (endExclusive == startInclusive || endExclusive > startInclusive != by > 0) {
return empty();
}
return new IteratorCharStream(new CharIteratorEx() {
private char next = startInclusive;
private int cnt = (endExclusive * 1 - startInclusive) / by + ((endExclusive * 1 - startInclusive) % by == 0 ? 0 : 1);
@Override
public boolean hasNext() {
return cnt > 0;
}
@Override
public char nextChar() {
if (cnt-- <= 0) {
throw new NoSuchElementException();
}
char result = next;
next += by;
return result;
}
@Override
public void advance(long n) {
cnt = n >= cnt ? 0 : cnt - (int) n;
next += n * by;
}
@Override
public long count() {
return cnt;
}
@Override
public char[] toArray() {
final char[] result = new char[cnt];
for (int i = 0; i < cnt; i++, next += by) {
result[i] = next;
}
cnt = 0;
return result;
}
});
}
public static CharStream rangeClosed(final char startInclusive, final char endInclusive) {
if (startInclusive > endInclusive) {
return empty();
} else if (startInclusive == endInclusive) {
return of(startInclusive);
}
return new IteratorCharStream(new CharIteratorEx() {
private char next = startInclusive;
private int cnt = endInclusive * 1 - startInclusive + 1;
@Override
public boolean hasNext() {
return cnt > 0;
}
@Override
public char nextChar() {
if (cnt-- <= 0) {
throw new NoSuchElementException();
}
return next++;
}
@Override
public void advance(long n) {
cnt = n >= cnt ? 0 : cnt - (int) n;
next += n;
}
@Override
public long count() {
return cnt;
}
@Override
public char[] toArray() {
final char[] result = new char[cnt];
for (int i = 0; i < cnt; i++) {
result[i] = next++;
}
cnt = 0;
return result;
}
});
}
public static CharStream rangeClosed(final char startInclusive, final char endInclusive, final int by) {
if (by == 0) {
throw new IllegalArgumentException("'by' can't be zero");
}
if (endInclusive == startInclusive) {
return of(startInclusive);
} else if (endInclusive > startInclusive != by > 0) {
return empty();
}
return new IteratorCharStream(new CharIteratorEx() {
private char next = startInclusive;
private int cnt = (endInclusive * 1 - startInclusive) / by + 1;
@Override
public boolean hasNext() {
return cnt > 0;
}
@Override
public char nextChar() {
if (cnt-- <= 0) {
throw new NoSuchElementException();
}
char result = next;
next += by;
return result;
}
@Override
public void advance(long n) {
cnt = n >= cnt ? 0 : cnt - (int) n;
next += n * by;
}
@Override
public long count() {
return cnt;
}
@Override
public char[] toArray() {
final char[] result = new char[cnt];
for (int i = 0; i < cnt; i++, next += by) {
result[i] = next;
}
cnt = 0;
return result;
}
});
}
public static CharStream repeat(final char element, final long n) {
N.checkArgNotNegative(n, "n");
if (n == 0) {
return empty();
} else if (n < 10) {
return of(Array.repeat(element, (int) n));
}
return new IteratorCharStream(new CharIteratorEx() {
private long cnt = n;
@Override
public boolean hasNext() {
return cnt > 0;
}
@Override
public char nextChar() {
if (cnt-- <= 0) {
throw new NoSuchElementException();
}
return element;
}
@Override
public void advance(long n) {
cnt = n >= cnt ? 0 : cnt - (int) n;
}
@Override
public long count() {
return cnt;
}
@Override
public char[] toArray() {
final char[] result = new char[(int) cnt];
for (int i = 0; i < cnt; i++) {
result[i] = element;
}
cnt = 0;
return result;
}
});
}
public static CharStream random() {
final int mod = Character.MAX_VALUE + 1;
return generate(() -> (char) RAND.nextInt(mod));
}
public static CharStream random(final char startInclusive, final char endExclusive) {
if (startInclusive >= endExclusive) {
throw new IllegalArgumentException("'startInclusive' must be less than 'endExclusive'");
}
final int mod = endExclusive - startInclusive;
return generate(() -> (char) (RAND.nextInt(mod) + startInclusive));
}
public static CharStream random(final char[] candicates) {
if (N.isNullOrEmpty(candicates)) {
return empty();
} else if (candicates.length >= Integer.MAX_VALUE) {
throw new IllegalArgumentException();
}
final int n = candicates.length;
return generate(() -> candicates[RAND.nextInt(n)]);
}
public static CharStream iterate(final BooleanSupplier hasNext, final CharSupplier next) {
N.checkArgNotNull(hasNext);
N.checkArgNotNull(next);
return new IteratorCharStream(new CharIteratorEx() {
private boolean hasNextVal = false;
@Override
public boolean hasNext() {
if (!hasNextVal) {
hasNextVal = hasNext.getAsBoolean();
}
return hasNextVal;
}
@Override
public char nextChar() {
if (!hasNextVal && !hasNext()) {
throw new NoSuchElementException();
}
hasNextVal = false;
return next.getAsChar();
}
});
}
public static CharStream iterate(final char init, final BooleanSupplier hasNext, final CharUnaryOperator f) {
N.checkArgNotNull(hasNext);
N.checkArgNotNull(f);
return new IteratorCharStream(new CharIteratorEx() {
private char t = 0;
private boolean isFirst = true;
private boolean hasNextVal = false;
@Override
public boolean hasNext() {
if (!hasNextVal) {
hasNextVal = hasNext.getAsBoolean();
}
return hasNextVal;
}
@Override
public char nextChar() {
if (!hasNextVal && !hasNext()) {
throw new NoSuchElementException();
}
hasNextVal = false;
if (isFirst) {
isFirst = false;
t = init;
} else {
t = f.applyAsChar(t);
}
return t;
}
});
}
/**
*
* @param init
* @param hasNext test if has next by hasNext.test(init) for first time and hasNext.test(f.apply(previous)) for remaining.
* @param f
* @return
*/
public static CharStream iterate(final char init, final CharPredicate hasNext, final CharUnaryOperator f) {
N.checkArgNotNull(hasNext);
N.checkArgNotNull(f);
return new IteratorCharStream(new CharIteratorEx() {
private char t = 0;
private char cur = 0;
private boolean isFirst = true;
private boolean hasMore = true;
private boolean hasNextVal = false;
@Override
public boolean hasNext() {
if (!hasNextVal && hasMore) {
if (isFirst) {
isFirst = false;
hasNextVal = hasNext.test(cur = init);
} else {
hasNextVal = hasNext.test(cur = f.applyAsChar(t));
}
if (!hasNextVal) {
hasMore = false;
}
}
return hasNextVal;
}
@Override
public char nextChar() {
if (!hasNextVal && !hasNext()) {
throw new NoSuchElementException();
}
t = cur;
hasNextVal = false;
return t;
}
});
}
public static CharStream iterate(final char init, final CharUnaryOperator f) {
N.checkArgNotNull(f);
return new IteratorCharStream(new CharIteratorEx() {
private char t = 0;
private boolean isFirst = true;
@Override
public boolean hasNext() {
return true;
}
@Override
public char nextChar() {
if (isFirst) {
isFirst = false;
t = init;
} else {
t = f.applyAsChar(t);
}
return t;
}
});
}
public static CharStream generate(final CharSupplier s) {
N.checkArgNotNull(s);
return new IteratorCharStream(new CharIteratorEx() {
@Override
public boolean hasNext() {
return true;
}
@Override
public char nextChar() {
return s.getAsChar();
}
});
}
@SafeVarargs
public static CharStream concat(final char[]... a) {
if (N.isNullOrEmpty(a)) {
return empty();
}
return concat(Arrays.asList(a));
}
@SafeVarargs
public static CharStream concat(final CharIterator... a) {
if (N.isNullOrEmpty(a)) {
return empty();
}
return concatIterators(Array.asList(a));
}
@SafeVarargs
public static CharStream concat(final CharStream... a) {
if (N.isNullOrEmpty(a)) {
return empty();
}
return concat(Array.asList(a));
}
@Beta
public static CharStream concat(final List c) {
if (N.isNullOrEmpty(c)) {
return empty();
}
return of(new CharIteratorEx() {
private final Iterator iter = c.iterator();
private char[] cur;
private int cursor = 0;
@Override
public boolean hasNext() {
while ((N.isNullOrEmpty(cur) || cursor >= cur.length) && iter.hasNext()) {
cur = iter.next();
cursor = 0;
}
return cur != null && cursor < cur.length;
}
@Override
public char nextChar() {
if ((cur == null || cursor >= cur.length) && !hasNext()) {
throw new NoSuchElementException();
}
return cur[cursor++];
}
});
}
public static CharStream concat(final Collection extends CharStream> c) {
return N.isNullOrEmpty(c) ? empty() : new IteratorCharStream(new CharIteratorEx() {
private final Iterator extends CharStream> iterators = c.iterator();
private CharStream cur;
private CharIterator iter;
@Override
public boolean hasNext() {
while ((iter == null || !iter.hasNext()) && iterators.hasNext()) {
if (cur != null) {
cur.close();
}
cur = iterators.next();
iter = cur == null ? null : cur.iteratorEx();
}
return iter != null && iter.hasNext();
}
@Override
public char nextChar() {
if ((iter == null || !iter.hasNext()) && !hasNext()) {
throw new NoSuchElementException();
}
return iter.nextChar();
}
}).onClose(newCloseHandler(c));
}
@Beta
public static CharStream concatIterators(final Collection extends CharIterator> c) {
if (N.isNullOrEmpty(c)) {
return empty();
}
return new IteratorCharStream(new CharIteratorEx() {
private final Iterator extends CharIterator> iter = c.iterator();
private CharIterator cur;
@Override
public boolean hasNext() {
while ((cur == null || !cur.hasNext()) && iter.hasNext()) {
cur = iter.next();
}
return cur != null && cur.hasNext();
}
@Override
public char nextChar() {
if ((cur == null || !cur.hasNext()) && !hasNext()) {
throw new NoSuchElementException();
}
return cur.nextChar();
}
});
}
/**
* Zip together the "a" and "b" arrays until one of them runs out of values.
* Each pair of values is combined into a single value using the supplied zipFunction function.
*
* @param a
* @param b
* @return
*/
public static CharStream zip(final char[] a, final char[] b, final CharBinaryOperator zipFunction) {
if (N.isNullOrEmpty(a) || N.isNullOrEmpty(b)) {
return empty();
}
return new IteratorCharStream(new CharIteratorEx() {
private final int len = N.min(N.len(a), N.len(b));
private int cursor = 0;
@Override
public boolean hasNext() {
return cursor < len;
}
@Override
public char nextChar() {
if (cursor >= len) {
throw new NoSuchElementException();
}
return zipFunction.applyAsChar(a[cursor], b[cursor++]);
}
});
}
/**
* Zip together the "a", "b" and "c" arrays until one of them runs out of values.
* Each triple of values is combined into a single value using the supplied zipFunction function.
*
* @param a
* @param b
* @param c
* @return
*/
public static CharStream zip(final char[] a, final char[] b, final char[] c, final CharTernaryOperator zipFunction) {
if (N.isNullOrEmpty(a) || N.isNullOrEmpty(b) || N.isNullOrEmpty(c)) {
return empty();
}
return new IteratorCharStream(new CharIteratorEx() {
private final int len = N.min(N.len(a), N.len(b), N.len(c));
private int cursor = 0;
@Override
public boolean hasNext() {
return cursor < len;
}
@Override
public char nextChar() {
if (cursor >= len) {
throw new NoSuchElementException();
}
return zipFunction.applyAsChar(a[cursor], b[cursor], c[cursor++]);
}
});
}
/**
* Zip together the "a" and "b" iterators until one of them runs out of values.
* Each pair of values is combined into a single value using the supplied zipFunction function.
*
* @param a
* @param b
* @return
*/
public static CharStream zip(final CharIterator a, final CharIterator b, final CharBinaryOperator zipFunction) {
return new IteratorCharStream(new CharIteratorEx() {
private final CharIterator iterA = a == null ? CharIterator.empty() : a;
private final CharIterator iterB = b == null ? CharIterator.empty() : b;
@Override
public boolean hasNext() {
return iterA.hasNext() && iterB.hasNext();
}
@Override
public char nextChar() {
return zipFunction.applyAsChar(iterA.nextChar(), iterB.nextChar());
}
});
}
/**
* Zip together the "a", "b" and "c" iterators until one of them runs out of values.
* Each triple of values is combined into a single value using the supplied zipFunction function.
*
* @param a
* @param b
* @return
*/
public static CharStream zip(final CharIterator a, final CharIterator b, final CharIterator c, final CharTernaryOperator zipFunction) {
return new IteratorCharStream(new CharIteratorEx() {
private final CharIterator iterA = a == null ? CharIterator.empty() : a;
private final CharIterator iterB = b == null ? CharIterator.empty() : b;
private final CharIterator iterC = c == null ? CharIterator.empty() : c;
@Override
public boolean hasNext() {
return iterA.hasNext() && iterB.hasNext() && iterC.hasNext();
}
@Override
public char nextChar() {
return zipFunction.applyAsChar(iterA.nextChar(), iterB.nextChar(), iterC.nextChar());
}
});
}
/**
* Zip together the "a" and "b" streams until one of them runs out of values.
* Each pair of values is combined into a single value using the supplied zipFunction function.
*
* @param a
* @param b
* @return
*/
public static CharStream zip(final CharStream a, final CharStream b, final CharBinaryOperator zipFunction) {
return zip(iterate(a), iterate(b), zipFunction).onClose(newCloseHandler(a, b));
}
/**
* Zip together the "a", "b" and "c" streams until one of them runs out of values.
* Each triple of values is combined into a single value using the supplied zipFunction function.
*
* @param a
* @param b
* @return
*/
public static CharStream zip(final CharStream a, final CharStream b, final CharStream c, final CharTernaryOperator zipFunction) {
return zip(iterate(a), iterate(b), iterate(c), zipFunction).onClose(newCloseHandler(Array.asList(a, b, c)));
}
/**
* Zip together the iterators until one of them runs out of values.
* Each array of values is combined into a single value using the supplied zipFunction function.
*
* @param c
* @param zipFunction
* @return
*/
public static CharStream zip(final Collection extends CharStream> c, final CharNFunction zipFunction) {
return Stream.zip(c, zipFunction).mapToChar(ToCharFunction.UNBOX);
}
/**
* Zip together the "a" and "b" iterators until all of them runs out of values.
* Each pair of values is combined into a single value using the supplied zipFunction function.
*
* @param a
* @param b
* @param valueForNoneA value to fill if "a" runs out of values first.
* @param valueForNoneB value to fill if "b" runs out of values first.
* @param zipFunction
* @return
*/
public static CharStream zip(final char[] a, final char[] b, final char valueForNoneA, final char valueForNoneB, final CharBinaryOperator zipFunction) {
if (N.isNullOrEmpty(a) && N.isNullOrEmpty(b)) {
return empty();
}
return new IteratorCharStream(new CharIteratorEx() {
private final int aLen = N.len(a), bLen = N.len(b), len = N.max(aLen, bLen);
private int cursor = 0;
private char ret = 0;
@Override
public boolean hasNext() {
return cursor < len;
}
@Override
public char nextChar() {
if (cursor >= len) {
throw new NoSuchElementException();
}
ret = zipFunction.applyAsChar(cursor < aLen ? a[cursor] : valueForNoneA, cursor < bLen ? b[cursor] : valueForNoneB);
cursor++;
return ret;
}
});
}
/**
* Zip together the "a", "b" and "c" iterators until all of them runs out of values.
* Each triple of values is combined into a single value using the supplied zipFunction function.
*
* @param a
* @param b
* @param c
* @param valueForNoneA value to fill if "a" runs out of values.
* @param valueForNoneB value to fill if "b" runs out of values.
* @param valueForNoneC value to fill if "c" runs out of values.
* @param zipFunction
* @return
*/
public static CharStream zip(final char[] a, final char[] b, final char[] c, final char valueForNoneA, final char valueForNoneB, final char valueForNoneC,
final CharTernaryOperator zipFunction) {
if (N.isNullOrEmpty(a) && N.isNullOrEmpty(b) && N.isNullOrEmpty(c)) {
return empty();
}
return new IteratorCharStream(new CharIteratorEx() {
private final int aLen = N.len(a), bLen = N.len(b), cLen = N.len(c), len = N.max(aLen, bLen, cLen);
private int cursor = 0;
private char ret = 0;
@Override
public boolean hasNext() {
return cursor < len;
}
@Override
public char nextChar() {
if (cursor >= len) {
throw new NoSuchElementException();
}
ret = zipFunction.applyAsChar(cursor < aLen ? a[cursor] : valueForNoneA, cursor < bLen ? b[cursor] : valueForNoneB,
cursor < cLen ? c[cursor] : valueForNoneC);
cursor++;
return ret;
}
});
}
/**
* Zip together the "a" and "b" iterators until all of them runs out of values.
* Each pair of values is combined into a single value using the supplied zipFunction function.
*
* @param a
* @param b
* @param valueForNoneA value to fill if "a" runs out of values first.
* @param valueForNoneB value to fill if "b" runs out of values first.
* @param zipFunction
* @return
*/
public static CharStream zip(final CharIterator a, final CharIterator b, final char valueForNoneA, final char valueForNoneB,
final CharBinaryOperator zipFunction) {
return new IteratorCharStream(new CharIteratorEx() {
private final CharIterator iterA = a == null ? CharIterator.empty() : a;
private final CharIterator iterB = b == null ? CharIterator.empty() : b;
@Override
public boolean hasNext() {
return iterA.hasNext() || iterB.hasNext();
}
@Override
public char nextChar() {
if (iterA.hasNext()) {
return zipFunction.applyAsChar(iterA.nextChar(), iterB.hasNext() ? iterB.nextChar() : valueForNoneB);
} else {
return zipFunction.applyAsChar(valueForNoneA, iterB.nextChar());
}
}
});
}
/**
* Zip together the "a", "b" and "c" iterators until all of them runs out of values.
* Each triple of values is combined into a single value using the supplied zipFunction function.
*
* @param a
* @param b
* @param c
* @param valueForNoneA value to fill if "a" runs out of values.
* @param valueForNoneB value to fill if "b" runs out of values.
* @param valueForNoneC value to fill if "c" runs out of values.
* @param zipFunction
* @return
*/
public static CharStream zip(final CharIterator a, final CharIterator b, final CharIterator c, final char valueForNoneA, final char valueForNoneB,
final char valueForNoneC, final CharTernaryOperator zipFunction) {
return new IteratorCharStream(new CharIteratorEx() {
private final CharIterator iterA = a == null ? CharIterator.empty() : a;
private final CharIterator iterB = b == null ? CharIterator.empty() : b;
private final CharIterator iterC = c == null ? CharIterator.empty() : c;
@Override
public boolean hasNext() {
return iterA.hasNext() || iterB.hasNext() || iterC.hasNext();
}
@Override
public char nextChar() {
if (iterA.hasNext()) {
return zipFunction.applyAsChar(iterA.nextChar(), iterB.hasNext() ? iterB.nextChar() : valueForNoneB,
iterC.hasNext() ? iterC.nextChar() : valueForNoneC);
} else if (iterB.hasNext()) {
return zipFunction.applyAsChar(valueForNoneA, iterB.nextChar(), iterC.hasNext() ? iterC.nextChar() : valueForNoneC);
} else {
return zipFunction.applyAsChar(valueForNoneA, valueForNoneB, iterC.nextChar());
}
}
});
}
/**
* Zip together the "a" and "b" iterators until all of them runs out of values.
* Each pair of values is combined into a single value using the supplied zipFunction function.
*
* @param a
* @param b
* @param valueForNoneA value to fill if "a" runs out of values first.
* @param valueForNoneB value to fill if "b" runs out of values first.
* @param zipFunction
* @return
*/
public static CharStream zip(final CharStream a, final CharStream b, final char valueForNoneA, final char valueForNoneB,
final CharBinaryOperator zipFunction) {
return zip(iterate(a), iterate(b), valueForNoneA, valueForNoneB, zipFunction).onClose(newCloseHandler(a, b));
}
/**
* Zip together the "a", "b" and "c" iterators until all of them runs out of values.
* Each triple of values is combined into a single value using the supplied zipFunction function.
*
* @param a
* @param b
* @param c
* @param valueForNoneA value to fill if "a" runs out of values.
* @param valueForNoneB value to fill if "b" runs out of values.
* @param valueForNoneC value to fill if "c" runs out of values.
* @param zipFunction
* @return
*/
public static CharStream zip(final CharStream a, final CharStream b, final CharStream c, final char valueForNoneA, final char valueForNoneB,
final char valueForNoneC, final CharTernaryOperator zipFunction) {
return zip(iterate(a), iterate(b), iterate(c), valueForNoneA, valueForNoneB, valueForNoneC, zipFunction)
.onClose(newCloseHandler(Array.asList(a, b, c)));
}
/**
* Zip together the iterators until all of them runs out of values.
* Each array of values is combined into a single value using the supplied zipFunction function.
*
* @param c
* @param valuesForNone value to fill for any iterator runs out of values.
* @param zipFunction
* @return
*/
public static CharStream zip(final Collection extends CharStream> c, final char[] valuesForNone, final CharNFunction zipFunction) {
return Stream.zip(c, valuesForNone, zipFunction).mapToChar(ToCharFunction.UNBOX);
}
/**
*
* @param a
* @param b
* @param nextSelector first parameter is selected if Nth.FIRST
is returned, otherwise the second parameter is selected.
* @return
*/
public static CharStream merge(final char[] a, final char[] b, final CharBiFunction nextSelector) {
if (N.isNullOrEmpty(a)) {
return of(b);
} else if (N.isNullOrEmpty(b)) {
return of(a);
}
return new IteratorCharStream(new CharIteratorEx() {
private final int lenA = a.length;
private final int lenB = b.length;
private int cursorA = 0;
private int cursorB = 0;
@Override
public boolean hasNext() {
return cursorA < lenA || cursorB < lenB;
}
@Override
public char nextChar() {
if (cursorA < lenA) {
if (cursorB < lenB) {
if (nextSelector.apply(a[cursorA], b[cursorB]) == MergeResult.TAKE_FIRST) {
return a[cursorA++];
} else {
return b[cursorB++];
}
} else {
return a[cursorA++];
}
} else if (cursorB < lenB) {
return b[cursorB++];
} else {
throw new NoSuchElementException();
}
}
});
}
/**
*
* @param a
* @param b
* @param c
* @param nextSelector first parameter is selected if Nth.FIRST
is returned, otherwise the second parameter is selected.
* @return
*/
public static CharStream merge(final char[] a, final char[] b, final char[] c, final CharBiFunction nextSelector) {
return merge(merge(a, b, nextSelector).iteratorEx(), CharStream.of(c).iteratorEx(), nextSelector);
}
/**
*
* @param a
* @param b
* @param nextSelector first parameter is selected if Nth.FIRST
is returned, otherwise the second parameter is selected.
* @return
*/
public static CharStream merge(final CharIterator a, final CharIterator b, final CharBiFunction nextSelector) {
return new IteratorCharStream(new CharIteratorEx() {
private final CharIterator iterA = a == null ? CharIterator.empty() : a;
private final CharIterator iterB = b == null ? CharIterator.empty() : b;
private char nextA = 0;
private char nextB = 0;
private boolean hasNextA = false;
private boolean hasNextB = false;
@Override
public boolean hasNext() {
return iterA.hasNext() || iterB.hasNext() || hasNextA || hasNextB;
}
@Override
public char nextChar() {
if (hasNextA) {
if (iterB.hasNext()) {
if (nextSelector.apply(nextA, (nextB = iterB.nextChar())) == MergeResult.TAKE_FIRST) {
hasNextA = false;
hasNextB = true;
return nextA;
} else {
return nextB;
}
} else {
hasNextA = false;
return nextA;
}
} else if (hasNextB) {
if (iterA.hasNext()) {
if (nextSelector.apply((nextA = iterA.nextChar()), nextB) == MergeResult.TAKE_FIRST) {
return nextA;
} else {
hasNextA = true;
hasNextB = false;
return nextB;
}
} else {
hasNextB = false;
return nextB;
}
} else if (iterA.hasNext()) {
if (iterB.hasNext()) {
if (nextSelector.apply((nextA = iterA.nextChar()), (nextB = iterB.nextChar())) == MergeResult.TAKE_FIRST) {
hasNextB = true;
return nextA;
} else {
hasNextA = true;
return nextB;
}
} else {
return iterA.nextChar();
}
} else if (iterB.hasNext()) {
return iterB.nextChar();
} else {
throw new NoSuchElementException();
}
}
});
}
/**
*
* @param a
* @param b
* @param c
* @param nextSelector first parameter is selected if Nth.FIRST
is returned, otherwise the second parameter is selected.
* @return
*/
public static CharStream merge(final CharIterator a, final CharIterator b, final CharIterator c, final CharBiFunction nextSelector) {
return merge(merge(a, b, nextSelector).iteratorEx(), c, nextSelector);
}
/**
*
* @param a
* @param b
* @param nextSelector first parameter is selected if Nth.FIRST
is returned, otherwise the second parameter is selected.
* @return
*/
public static CharStream merge(final CharStream a, final CharStream b, final CharBiFunction nextSelector) {
return merge(iterate(a), iterate(b), nextSelector).onClose(newCloseHandler(a, b));
}
/**
*
* @param a
* @param b
* @param c
* @param nextSelector first parameter is selected if Nth.FIRST
is returned, otherwise the second parameter is selected.
* @return
*/
public static CharStream merge(final CharStream a, final CharStream b, final CharStream c, final CharBiFunction nextSelector) {
return merge(merge(a, b, nextSelector), c, nextSelector);
}
/**
*
* @param c
* @param nextSelector first parameter is selected if Nth.FIRST
is returned, otherwise the second parameter is selected.
* @return
*/
public static CharStream merge(final Collection extends CharStream> c, final CharBiFunction nextSelector) {
if (N.isNullOrEmpty(c)) {
return empty();
} else if (c.size() == 1) {
return c.iterator().next();
} else if (c.size() == 2) {
final Iterator extends CharStream> iter = c.iterator();
return merge(iter.next(), iter.next(), nextSelector);
}
final Iterator extends CharStream> iter = c.iterator();
CharStream result = merge(iter.next(), iter.next(), nextSelector);
while (iter.hasNext()) {
result = merge(result, iter.next(), nextSelector);
}
return result;
}
/**
* All the elements from each input {@code Collection/Iterator/Stream} will be merged into two queues by multiple threads.
* Then these two new queues will be merged into one {@code Iterator/Stream} by one thread.
* So it's not totally lazy evaluation and may cause out of memory error if there are too many elements merged into the new queues.
* Consider using {@code merge}, which is totally lazy evaluation.
*
* @param c
* @param nextSelector first parameter is selected if Nth.FIRST
is returned, otherwise the second parameter is selected.
* @return
*/
public static CharStream parallelMerge(final Collection extends CharStream> c, final CharBiFunction nextSelector) {
return parallelMerge(c, nextSelector, DEFAULT_MAX_THREAD_NUM);
}
/**
* All the elements from each input {@code Collection/Iterator/Stream} will be merged into two queues by multiple threads.
* Then these two new queues will be merged into one {@code Iterator/Stream} by one thread.
* So it's not totally lazy evaluation and may cause out of memory error if there are too many elements merged into the new queues.
* Consider using {@code merge}, which is totally lazy evaluation.
*
* @param c
* @param nextSelector first parameter is selected if Nth.FIRST
is returned, otherwise the second parameter is selected.
* @param maxThreadNum
* @return
*/
public static CharStream parallelMerge(final Collection extends CharStream> c, final CharBiFunction nextSelector, final int maxThreadNum) {
N.checkArgument(maxThreadNum > 0, "'maxThreadNum' must not less than 1");
if (maxThreadNum <= 1) {
return merge(c, nextSelector);
} else if (N.isNullOrEmpty(c)) {
return empty();
} else if (c.size() == 1) {
return c.iterator().next();
} else if (c.size() == 2) {
final Iterator extends CharStream> iter = c.iterator();
return merge(iter.next(), iter.next(), nextSelector);
} else if (c.size() == 3) {
final Iterator extends CharStream> iter = c.iterator();
return merge(iter.next(), iter.next(), iter.next(), nextSelector);
}
final Supplier supplier = () -> {
final Queue queue = N.newLinkedList();
queue.addAll(c);
final Holder eHolder = new Holder<>();
final MutableInt cnt = MutableInt.of(c.size());
final List> futureList = new ArrayList<>(c.size() - 1);
final int threadNum = N.min(maxThreadNum, c.size() / 2 + 1);
AsyncExecutor asyncExecutorToUse = checkAsyncExecutor(DEFAULT_ASYNC_EXECUTOR, threadNum, 0);
for (int i = 0; i < threadNum; i++) {
asyncExecutorToUse = execute(asyncExecutorToUse, threadNum, 0, i, futureList, () -> {
CharStream a = null;
CharStream b = null;
CharStream c1 = null;
try {
while (eHolder.value() == null) {
synchronized (queue) {
if (cnt.intValue() > 2 && queue.size() > 1) {
a = queue.poll();
b = queue.poll();
cnt.decrement();
} else {
break;
}
}
c1 = CharStream.of(merge(a, b, nextSelector).toArray());
synchronized (queue) {
queue.offer(c1);
}
}
} catch (Throwable e) {
setError(eHolder, e);
}
});
}
completeAndShutdownTempExecutor(futureList, eHolder, c, asyncExecutorToUse);
return merge(queue.poll(), queue.poll(), nextSelector);
};
return Stream.just(supplier).flatMapToChar(Supplier::get);
}
public static abstract class CharStreamEx extends CharStream {
private CharStreamEx(boolean sorted, Collection closeHandlers) {
super(sorted, closeHandlers);
}
}
}