package one.util.streamex; import java.util.Iterator; import java.util.PrimitiveIterator; import java.util.Spliterator; import java.util.function.Consumer; import java.util.function.DoubleConsumer; import java.util.function.IntConsumer; import java.util.function.LongConsumer; import java.util.stream.Stream; import java.util.stream.StreamSupport; import static one.util.streamex.StreamExInternals.*; /* package */abstract class UnknownSizeSpliterator<T, S extends UnknownSizeSpliterator<? extends T, S, I>, I extends Iterator<? extends T>> implements Spliterator<T> { static final int BATCH_UNIT = 1 << 10; // batch array size increment static final int MAX_BATCH = 1 << 25; // max batch array size; /** * Optimize the stream created on IteratorSpliterator replacing it with * UnknownSizeSpliterator. * * @param stream original stream * @return either original or optimized stream */ @SuppressWarnings("unchecked") static <T> Stream<T> optimize(Stream<T> stream) { if (SOURCE_SPLITERATOR == null || SPLITERATOR_ITERATOR == null) return stream; Iterator<T> it = null; try { Spliterator<T> spliterator = (Spliterator<T>) SOURCE_SPLITERATOR.get(stream); if (spliterator != null && !spliterator.hasCharacteristics(SIZED) && spliterator.getClass().getName().equals("java.util.Spliterators$IteratorSpliterator")) { it = (Iterator<T>) SPLITERATOR_ITERATOR.get(spliterator); } } catch (IllegalArgumentException | IllegalAccessException e) { // ignore } if (it == null) return stream; //noinspection ResultOfMethodCallIgnored stream.spliterator(); // consume stream return StreamSupport.stream(new USOfRef<>(it), stream.isParallel()).onClose(stream::close); } I it; int index, fence; long est = Long.MAX_VALUE; UnknownSizeSpliterator(I iterator) { this.it = iterator; } UnknownSizeSpliterator(int index, int fence) { this.index = index; this.fence = fence; } int getN() { int n = fence + BATCH_UNIT; return n > MAX_BATCH ? MAX_BATCH : n; } S correctSize(S prefix) { if (this.it != null) prefix.est = Long.MAX_VALUE - 1; else { prefix.est = this.est / 2; this.est -= prefix.est; } return prefix; } @Override public long estimateSize() { return est; } @Override public int characteristics() { return ORDERED; } static class USOfRef<T> extends UnknownSizeSpliterator<T, USOfRef<T>, Iterator<? extends T>> { Object[] array; USOfRef(Iterator<? extends T> iterator) { super(iterator); } USOfRef(Object[] array, int index, int fence) { super(index, fence); this.array = array; } @Override public Spliterator<T> trySplit() { Iterator<? extends T> i = it; if (i != null) { int n = getN(); Object[] a = new Object[n]; int j = 0; while (i.hasNext() && j < n) { a[j++] = i.next(); } fence = j; if (i.hasNext()) { return correctSize(new USOfRef<>(a, 0, j)); } it = null; array = a; } int lo = index, mid = (lo + fence) >>> 1; return (lo >= mid) ? null : correctSize(new USOfRef<>(array, lo, index = mid)); } @Override public void forEachRemaining(Consumer<? super T> action) { if (it != null) it.forEachRemaining(action); else { Object[] a = array; int i = index, hi = fence; while (i < hi) { @SuppressWarnings("unchecked") T t = (T) a[i++]; action.accept(t); } } index = fence; est = 0; } @Override public boolean tryAdvance(Consumer<? super T> action) { if (it != null) { if (it.hasNext()) { action.accept(it.next()); return true; } it = null; index = fence; } else if (index < fence) { @SuppressWarnings("unchecked") T t = (T) array[index++]; action.accept(t); return true; } est = 0; return false; } } static class USOfInt extends UnknownSizeSpliterator<Integer, USOfInt, PrimitiveIterator.OfInt> implements Spliterator.OfInt { int[] array; USOfInt(PrimitiveIterator.OfInt iterator) { super(iterator); } USOfInt(int[] array, int index, int fence) { super(index, fence); this.array = array; } @Override public Spliterator.OfInt trySplit() { PrimitiveIterator.OfInt i = it; if (i != null) { int n = getN(); int[] a = new int[n]; int j = 0; while (i.hasNext() && j < n) { a[j++] = i.next(); } fence = j; if (i.hasNext()) { return correctSize(new USOfInt(a, 0, j)); } it = null; array = a; } int lo = index, mid = (lo + fence) >>> 1; return (lo >= mid) ? null : correctSize(new USOfInt(array, lo, index = mid)); } @Override public void forEachRemaining(IntConsumer action) { if (it != null) it.forEachRemaining(action); else { int[] a = array; int i = index, hi = fence; while (i < hi) { action.accept(a[i++]); } } index = fence; est = 0; } @Override public boolean tryAdvance(IntConsumer action) { if (it != null) { if (it.hasNext()) { action.accept(it.nextInt()); return true; } it = null; index = fence; } else if (index < fence) { action.accept(array[index++]); return true; } est = 0; return false; } } static class USOfLong extends UnknownSizeSpliterator<Long, USOfLong, PrimitiveIterator.OfLong> implements Spliterator.OfLong { long[] array; USOfLong(PrimitiveIterator.OfLong iterator) { super(iterator); } USOfLong(long[] array, int index, int fence) { super(index, fence); this.array = array; } @Override public Spliterator.OfLong trySplit() { PrimitiveIterator.OfLong i = it; if (i != null) { int n = getN(); long[] a = new long[n]; int j = 0; while (i.hasNext() && j < n) { a[j++] = i.next(); } fence = j; if (i.hasNext()) { return correctSize(new USOfLong(a, 0, j)); } it = null; array = a; } int lo = index, mid = (lo + fence) >>> 1; return (lo >= mid) ? null : correctSize(new USOfLong(array, lo, index = mid)); } @Override public void forEachRemaining(LongConsumer action) { if (it != null) it.forEachRemaining(action); else { long[] a = array; int i = index, hi = fence; while (i < hi) { action.accept(a[i++]); } } index = fence; est = 0; } @Override public boolean tryAdvance(LongConsumer action) { if (it != null) { if (it.hasNext()) { action.accept(it.nextLong()); return true; } it = null; index = fence; } else if (index < fence) { action.accept(array[index++]); return true; } est = 0; return false; } } static class USOfDouble extends UnknownSizeSpliterator<Double, USOfDouble, PrimitiveIterator.OfDouble> implements Spliterator.OfDouble { double[] array; USOfDouble(PrimitiveIterator.OfDouble iterator) { super(iterator); } USOfDouble(double[] array, int index, int fence) { super(index, fence); this.array = array; } @Override public Spliterator.OfDouble trySplit() { PrimitiveIterator.OfDouble i = it; if (i != null) { int n = getN(); double[] a = new double[n]; int j = 0; while (i.hasNext() && j < n) { a[j++] = i.next(); } fence = j; if (i.hasNext()) { return correctSize(new USOfDouble(a, 0, j)); } it = null; array = a; } int lo = index, mid = (lo + fence) >>> 1; return (lo >= mid) ? null : correctSize(new USOfDouble(array, lo, index = mid)); } @Override public void forEachRemaining(DoubleConsumer action) { if (it != null) it.forEachRemaining(action); else { double[] a = array; int i = index, hi = fence; while (i < hi) { action.accept(a[i++]); } } index = fence; est = 0; } @Override public boolean tryAdvance(DoubleConsumer action) { if (it != null) { if (it.hasNext()) { action.accept(it.nextDouble()); return true; } it = null; index = fence; } else if (index < fence) { action.accept(array[index++]); return true; } est = 0; return false; } } }