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

com.google.common.collect.Streams Maven / Gradle / Ivy

Go to download

Guava is a suite of core and expanded libraries that include utility classes, Google's collections, I/O classes, and much more.

There is a newer version: 33.1.0-jre
Show newest version
/*
 * Copyright (C) 2015 The Guava Authors
 *
 * 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.google.common.collect;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.NullnessCasts.uncheckedCastNullableTToT;
import static java.lang.Math.min;
import static java.util.Objects.requireNonNull;

import com.google.common.annotations.Beta;
import com.google.common.annotations.GwtCompatible;
import com.google.common.math.LongMath;
import com.google.errorprone.annotations.InlineMe;
import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Deque;
import java.util.Iterator;
import java.util.OptionalDouble;
import java.util.OptionalInt;
import java.util.OptionalLong;
import java.util.PrimitiveIterator;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.Spliterators.AbstractSpliterator;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.DoubleConsumer;
import java.util.function.IntConsumer;
import java.util.function.LongConsumer;
import java.util.stream.BaseStream;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import javax.annotation.CheckForNull;
import org.checkerframework.checker.nullness.qual.Nullable;

/**
 * Static utility methods related to {@code Stream} instances.
 *
 * @since 21.0
 */
@GwtCompatible
@ElementTypesAreNonnullByDefault
public final class Streams {
  /**
   * Returns a sequential {@link Stream} of the contents of {@code iterable}, delegating to {@link
   * Collection#stream} if possible.
   */
  public static  Stream stream(Iterable iterable) {
    return (iterable instanceof Collection)
        ? ((Collection) iterable).stream()
        : StreamSupport.stream(iterable.spliterator(), false);
  }

  /**
   * Returns {@link Collection#stream}.
   *
   * @deprecated There is no reason to use this; just invoke {@code collection.stream()} directly.
   */
  @Beta
  @Deprecated
  @InlineMe(replacement = "collection.stream()")
  public static  Stream stream(Collection collection) {
    return collection.stream();
  }

  /**
   * Returns a sequential {@link Stream} of the remaining contents of {@code iterator}. Do not use
   * {@code iterator} directly after passing it to this method.
   */
  @Beta
  public static  Stream stream(Iterator iterator) {
    return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, 0), false);
  }

  /**
   * If a value is present in {@code optional}, returns a stream containing only that element,
   * otherwise returns an empty stream.
   */
  @Beta
  public static  Stream stream(com.google.common.base.Optional optional) {
    return optional.isPresent() ? Stream.of(optional.get()) : Stream.empty();
  }

  /**
   * If a value is present in {@code optional}, returns a stream containing only that element,
   * otherwise returns an empty stream.
   *
   * 

Java 9 users: use {@code optional.stream()} instead. */ @Beta @InlineMe(replacement = "optional.stream()") @com.google.errorprone.annotations.InlineMeValidationDisabled("Java 9+ API only") public static Stream stream(java.util.Optional optional) { return optional.isPresent() ? Stream.of(optional.get()) : Stream.empty(); } /** * If a value is present in {@code optional}, returns a stream containing only that element, * otherwise returns an empty stream. * *

Java 9 users: use {@code optional.stream()} instead. */ @Beta @InlineMe(replacement = "optional.stream()") @com.google.errorprone.annotations.InlineMeValidationDisabled("Java 9+ API only") public static IntStream stream(OptionalInt optional) { return optional.isPresent() ? IntStream.of(optional.getAsInt()) : IntStream.empty(); } /** * If a value is present in {@code optional}, returns a stream containing only that element, * otherwise returns an empty stream. * *

Java 9 users: use {@code optional.stream()} instead. */ @Beta @InlineMe(replacement = "optional.stream()") @com.google.errorprone.annotations.InlineMeValidationDisabled("Java 9+ API only") public static LongStream stream(OptionalLong optional) { return optional.isPresent() ? LongStream.of(optional.getAsLong()) : LongStream.empty(); } /** * If a value is present in {@code optional}, returns a stream containing only that element, * otherwise returns an empty stream. * *

Java 9 users: use {@code optional.stream()} instead. */ @Beta @InlineMe(replacement = "optional.stream()") @com.google.errorprone.annotations.InlineMeValidationDisabled("Java 9+ API only") public static DoubleStream stream(OptionalDouble optional) { return optional.isPresent() ? DoubleStream.of(optional.getAsDouble()) : DoubleStream.empty(); } private static void closeAll(BaseStream[] toClose) { for (BaseStream stream : toClose) { // TODO(b/80534298): Catch exceptions, rethrowing later with extras as suppressed exceptions. stream.close(); } } /** * Returns a {@link Stream} containing the elements of the first stream, followed by the elements * of the second stream, and so on. * *

This is equivalent to {@code Stream.of(streams).flatMap(stream -> stream)}, but the returned * stream may perform better. * * @see Stream#concat(Stream, Stream) */ @SafeVarargs public static Stream concat(Stream... streams) { // TODO(lowasser): consider an implementation that can support SUBSIZED boolean isParallel = false; int characteristics = Spliterator.ORDERED | Spliterator.SIZED | Spliterator.NONNULL; long estimatedSize = 0L; ImmutableList.Builder> splitrsBuilder = new ImmutableList.Builder<>(streams.length); for (Stream stream : streams) { isParallel |= stream.isParallel(); Spliterator splitr = stream.spliterator(); splitrsBuilder.add(splitr); characteristics &= splitr.characteristics(); estimatedSize = LongMath.saturatedAdd(estimatedSize, splitr.estimateSize()); } return StreamSupport.stream( CollectSpliterators.flatMap( splitrsBuilder.build().spliterator(), splitr -> (Spliterator) splitr, characteristics, estimatedSize), isParallel) .onClose(() -> closeAll(streams)); } /** * Returns an {@link IntStream} containing the elements of the first stream, followed by the * elements of the second stream, and so on. * *

This is equivalent to {@code Stream.of(streams).flatMapToInt(stream -> stream)}, but the * returned stream may perform better. * * @see IntStream#concat(IntStream, IntStream) */ public static IntStream concat(IntStream... streams) { boolean isParallel = false; int characteristics = Spliterator.ORDERED | Spliterator.SIZED | Spliterator.NONNULL; long estimatedSize = 0L; ImmutableList.Builder splitrsBuilder = new ImmutableList.Builder<>(streams.length); for (IntStream stream : streams) { isParallel |= stream.isParallel(); Spliterator.OfInt splitr = stream.spliterator(); splitrsBuilder.add(splitr); characteristics &= splitr.characteristics(); estimatedSize = LongMath.saturatedAdd(estimatedSize, splitr.estimateSize()); } return StreamSupport.intStream( CollectSpliterators.flatMapToInt( splitrsBuilder.build().spliterator(), splitr -> splitr, characteristics, estimatedSize), isParallel) .onClose(() -> closeAll(streams)); } /** * Returns a {@link LongStream} containing the elements of the first stream, followed by the * elements of the second stream, and so on. * *

This is equivalent to {@code Stream.of(streams).flatMapToLong(stream -> stream)}, but the * returned stream may perform better. * * @see LongStream#concat(LongStream, LongStream) */ public static LongStream concat(LongStream... streams) { boolean isParallel = false; int characteristics = Spliterator.ORDERED | Spliterator.SIZED | Spliterator.NONNULL; long estimatedSize = 0L; ImmutableList.Builder splitrsBuilder = new ImmutableList.Builder<>(streams.length); for (LongStream stream : streams) { isParallel |= stream.isParallel(); Spliterator.OfLong splitr = stream.spliterator(); splitrsBuilder.add(splitr); characteristics &= splitr.characteristics(); estimatedSize = LongMath.saturatedAdd(estimatedSize, splitr.estimateSize()); } return StreamSupport.longStream( CollectSpliterators.flatMapToLong( splitrsBuilder.build().spliterator(), splitr -> splitr, characteristics, estimatedSize), isParallel) .onClose(() -> closeAll(streams)); } /** * Returns a {@link DoubleStream} containing the elements of the first stream, followed by the * elements of the second stream, and so on. * *

This is equivalent to {@code Stream.of(streams).flatMapToDouble(stream -> stream)}, but the * returned stream may perform better. * * @see DoubleStream#concat(DoubleStream, DoubleStream) */ public static DoubleStream concat(DoubleStream... streams) { boolean isParallel = false; int characteristics = Spliterator.ORDERED | Spliterator.SIZED | Spliterator.NONNULL; long estimatedSize = 0L; ImmutableList.Builder splitrsBuilder = new ImmutableList.Builder<>(streams.length); for (DoubleStream stream : streams) { isParallel |= stream.isParallel(); Spliterator.OfDouble splitr = stream.spliterator(); splitrsBuilder.add(splitr); characteristics &= splitr.characteristics(); estimatedSize = LongMath.saturatedAdd(estimatedSize, splitr.estimateSize()); } return StreamSupport.doubleStream( CollectSpliterators.flatMapToDouble( splitrsBuilder.build().spliterator(), splitr -> splitr, characteristics, estimatedSize), isParallel) .onClose(() -> closeAll(streams)); } /** * Returns a stream in which each element is the result of passing the corresponding element of * each of {@code streamA} and {@code streamB} to {@code function}. * *

For example: * *

{@code
   * Streams.zip(
   *   Stream.of("foo1", "foo2", "foo3"),
   *   Stream.of("bar1", "bar2"),
   *   (arg1, arg2) -> arg1 + ":" + arg2)
   * }
* *

will return {@code Stream.of("foo1:bar1", "foo2:bar2")}. * *

The resulting stream will only be as long as the shorter of the two input streams; if one * stream is longer, its extra elements will be ignored. * *

Note that if you are calling {@link Stream#forEach} on the resulting stream, you might want * to consider using {@link #forEachPair} instead of this method. * *

Performance note: The resulting stream is not efficiently splittable. * This may harm parallel performance. */ @Beta public static Stream zip( Stream streamA, Stream streamB, BiFunction function) { checkNotNull(streamA); checkNotNull(streamB); checkNotNull(function); boolean isParallel = streamA.isParallel() || streamB.isParallel(); // same as Stream.concat Spliterator splitrA = streamA.spliterator(); Spliterator splitrB = streamB.spliterator(); int characteristics = splitrA.characteristics() & splitrB.characteristics() & (Spliterator.SIZED | Spliterator.ORDERED); Iterator itrA = Spliterators.iterator(splitrA); Iterator itrB = Spliterators.iterator(splitrB); return StreamSupport.stream( new AbstractSpliterator( min(splitrA.estimateSize(), splitrB.estimateSize()), characteristics) { @Override public boolean tryAdvance(Consumer action) { if (itrA.hasNext() && itrB.hasNext()) { action.accept(function.apply(itrA.next(), itrB.next())); return true; } return false; } }, isParallel) .onClose(streamA::close) .onClose(streamB::close); } /** * Invokes {@code consumer} once for each pair of corresponding elements in {@code streamA} * and {@code streamB}. If one stream is longer than the other, the extra elements are silently * ignored. Elements passed to the consumer are guaranteed to come from the same position in their * respective source streams. For example: * *

{@code
   * Streams.forEachPair(
   *   Stream.of("foo1", "foo2", "foo3"),
   *   Stream.of("bar1", "bar2"),
   *   (arg1, arg2) -> System.out.println(arg1 + ":" + arg2)
   * }
* *

will print: * *

{@code
   * foo1:bar1
   * foo2:bar2
   * }
* *

Warning: If either supplied stream is a parallel stream, the same correspondence * between elements will be made, but the order in which those pairs of elements are passed to the * consumer is not defined. * *

Note that many usages of this method can be replaced with simpler calls to {@link #zip}. * This method behaves equivalently to {@linkplain #zip zipping} the stream elements into * temporary pair objects and then using {@link Stream#forEach} on that stream. * * @since 22.0 */ @Beta public static void forEachPair( Stream streamA, Stream streamB, BiConsumer consumer) { checkNotNull(consumer); if (streamA.isParallel() || streamB.isParallel()) { zip(streamA, streamB, TemporaryPair::new).forEach(pair -> consumer.accept(pair.a, pair.b)); } else { Iterator iterA = streamA.iterator(); Iterator iterB = streamB.iterator(); while (iterA.hasNext() && iterB.hasNext()) { consumer.accept(iterA.next(), iterB.next()); } } } // Use this carefully - it doesn't implement value semantics private static class TemporaryPair { @ParametricNullness final A a; @ParametricNullness final B b; TemporaryPair(@ParametricNullness A a, @ParametricNullness B b) { this.a = a; this.b = b; } } /** * Returns a stream consisting of the results of applying the given function to the elements of * {@code stream} and their indices in the stream. For example, * *

{@code
   * mapWithIndex(
   *     Stream.of("a", "b", "c"),
   *     (e, index) -> index + ":" + e)
   * }
* *

would return {@code Stream.of("0:a", "1:b", "2:c")}. * *

The resulting stream is efficiently splittable * if and only if {@code stream} was efficiently splittable and its underlying spliterator * reported {@link Spliterator#SUBSIZED}. This is generally the case if the underlying stream * comes from a data structure supporting efficient indexed random access, typically an array or * list. * *

The order of the resulting stream is defined if and only if the order of the original stream * was defined. */ public static Stream mapWithIndex( Stream stream, FunctionWithIndex function) { checkNotNull(stream); checkNotNull(function); boolean isParallel = stream.isParallel(); Spliterator fromSpliterator = stream.spliterator(); if (!fromSpliterator.hasCharacteristics(Spliterator.SUBSIZED)) { Iterator fromIterator = Spliterators.iterator(fromSpliterator); return StreamSupport.stream( new AbstractSpliterator( fromSpliterator.estimateSize(), fromSpliterator.characteristics() & (Spliterator.ORDERED | Spliterator.SIZED)) { long index = 0; @Override public boolean tryAdvance(Consumer action) { if (fromIterator.hasNext()) { action.accept(function.apply(fromIterator.next(), index++)); return true; } return false; } }, isParallel) .onClose(stream::close); } class Splitr extends MapWithIndexSpliterator, R, Splitr> implements Consumer { @CheckForNull T holder; Splitr(Spliterator splitr, long index) { super(splitr, index); } @Override public void accept(@ParametricNullness T t) { this.holder = t; } @Override public boolean tryAdvance(Consumer action) { if (fromSpliterator.tryAdvance(this)) { try { // The cast is safe because tryAdvance puts a T into `holder`. action.accept(function.apply(uncheckedCastNullableTToT(holder), index++)); return true; } finally { holder = null; } } return false; } @Override Splitr createSplit(Spliterator from, long i) { return new Splitr(from, i); } } return StreamSupport.stream(new Splitr(fromSpliterator, 0), isParallel).onClose(stream::close); } /** * Returns a stream consisting of the results of applying the given function to the elements of * {@code stream} and their indexes in the stream. For example, * *

{@code
   * mapWithIndex(
   *     IntStream.of(10, 11, 12),
   *     (e, index) -> index + ":" + e)
   * }
* *

...would return {@code Stream.of("0:10", "1:11", "2:12")}. * *

The resulting stream is efficiently splittable * if and only if {@code stream} was efficiently splittable and its underlying spliterator * reported {@link Spliterator#SUBSIZED}. This is generally the case if the underlying stream * comes from a data structure supporting efficient indexed random access, typically an array or * list. * *

The order of the resulting stream is defined if and only if the order of the original stream * was defined. */ public static Stream mapWithIndex( IntStream stream, IntFunctionWithIndex function) { checkNotNull(stream); checkNotNull(function); boolean isParallel = stream.isParallel(); Spliterator.OfInt fromSpliterator = stream.spliterator(); if (!fromSpliterator.hasCharacteristics(Spliterator.SUBSIZED)) { PrimitiveIterator.OfInt fromIterator = Spliterators.iterator(fromSpliterator); return StreamSupport.stream( new AbstractSpliterator( fromSpliterator.estimateSize(), fromSpliterator.characteristics() & (Spliterator.ORDERED | Spliterator.SIZED)) { long index = 0; @Override public boolean tryAdvance(Consumer action) { if (fromIterator.hasNext()) { action.accept(function.apply(fromIterator.nextInt(), index++)); return true; } return false; } }, isParallel) .onClose(stream::close); } class Splitr extends MapWithIndexSpliterator implements IntConsumer, Spliterator { int holder; Splitr(Spliterator.OfInt splitr, long index) { super(splitr, index); } @Override public void accept(int t) { this.holder = t; } @Override public boolean tryAdvance(Consumer action) { if (fromSpliterator.tryAdvance(this)) { action.accept(function.apply(holder, index++)); return true; } return false; } @Override Splitr createSplit(Spliterator.OfInt from, long i) { return new Splitr(from, i); } } return StreamSupport.stream(new Splitr(fromSpliterator, 0), isParallel).onClose(stream::close); } /** * Returns a stream consisting of the results of applying the given function to the elements of * {@code stream} and their indexes in the stream. For example, * *

{@code
   * mapWithIndex(
   *     LongStream.of(10, 11, 12),
   *     (e, index) -> index + ":" + e)
   * }
* *

...would return {@code Stream.of("0:10", "1:11", "2:12")}. * *

The resulting stream is efficiently splittable * if and only if {@code stream} was efficiently splittable and its underlying spliterator * reported {@link Spliterator#SUBSIZED}. This is generally the case if the underlying stream * comes from a data structure supporting efficient indexed random access, typically an array or * list. * *

The order of the resulting stream is defined if and only if the order of the original stream * was defined. */ public static Stream mapWithIndex( LongStream stream, LongFunctionWithIndex function) { checkNotNull(stream); checkNotNull(function); boolean isParallel = stream.isParallel(); Spliterator.OfLong fromSpliterator = stream.spliterator(); if (!fromSpliterator.hasCharacteristics(Spliterator.SUBSIZED)) { PrimitiveIterator.OfLong fromIterator = Spliterators.iterator(fromSpliterator); return StreamSupport.stream( new AbstractSpliterator( fromSpliterator.estimateSize(), fromSpliterator.characteristics() & (Spliterator.ORDERED | Spliterator.SIZED)) { long index = 0; @Override public boolean tryAdvance(Consumer action) { if (fromIterator.hasNext()) { action.accept(function.apply(fromIterator.nextLong(), index++)); return true; } return false; } }, isParallel) .onClose(stream::close); } class Splitr extends MapWithIndexSpliterator implements LongConsumer, Spliterator { long holder; Splitr(Spliterator.OfLong splitr, long index) { super(splitr, index); } @Override public void accept(long t) { this.holder = t; } @Override public boolean tryAdvance(Consumer action) { if (fromSpliterator.tryAdvance(this)) { action.accept(function.apply(holder, index++)); return true; } return false; } @Override Splitr createSplit(Spliterator.OfLong from, long i) { return new Splitr(from, i); } } return StreamSupport.stream(new Splitr(fromSpliterator, 0), isParallel).onClose(stream::close); } /** * Returns a stream consisting of the results of applying the given function to the elements of * {@code stream} and their indexes in the stream. For example, * *

{@code
   * mapWithIndex(
   *     DoubleStream.of(0.0, 1.0, 2.0)
   *     (e, index) -> index + ":" + e)
   * }
* *

...would return {@code Stream.of("0:0.0", "1:1.0", "2:2.0")}. * *

The resulting stream is efficiently splittable * if and only if {@code stream} was efficiently splittable and its underlying spliterator * reported {@link Spliterator#SUBSIZED}. This is generally the case if the underlying stream * comes from a data structure supporting efficient indexed random access, typically an array or * list. * *

The order of the resulting stream is defined if and only if the order of the original stream * was defined. */ public static Stream mapWithIndex( DoubleStream stream, DoubleFunctionWithIndex function) { checkNotNull(stream); checkNotNull(function); boolean isParallel = stream.isParallel(); Spliterator.OfDouble fromSpliterator = stream.spliterator(); if (!fromSpliterator.hasCharacteristics(Spliterator.SUBSIZED)) { PrimitiveIterator.OfDouble fromIterator = Spliterators.iterator(fromSpliterator); return StreamSupport.stream( new AbstractSpliterator( fromSpliterator.estimateSize(), fromSpliterator.characteristics() & (Spliterator.ORDERED | Spliterator.SIZED)) { long index = 0; @Override public boolean tryAdvance(Consumer action) { if (fromIterator.hasNext()) { action.accept(function.apply(fromIterator.nextDouble(), index++)); return true; } return false; } }, isParallel) .onClose(stream::close); } class Splitr extends MapWithIndexSpliterator implements DoubleConsumer, Spliterator { double holder; Splitr(Spliterator.OfDouble splitr, long index) { super(splitr, index); } @Override public void accept(double t) { this.holder = t; } @Override public boolean tryAdvance(Consumer action) { if (fromSpliterator.tryAdvance(this)) { action.accept(function.apply(holder, index++)); return true; } return false; } @Override Splitr createSplit(Spliterator.OfDouble from, long i) { return new Splitr(from, i); } } return StreamSupport.stream(new Splitr(fromSpliterator, 0), isParallel).onClose(stream::close); } /** * An analogue of {@link java.util.function.Function} also accepting an index. * *

This interface is only intended for use by callers of {@link #mapWithIndex(Stream, * FunctionWithIndex)}. * * @since 21.0 */ public interface FunctionWithIndex { /** Applies this function to the given argument and its index within a stream. */ @ParametricNullness R apply(@ParametricNullness T from, long index); } private abstract static class MapWithIndexSpliterator< F extends Spliterator, R extends @Nullable Object, S extends MapWithIndexSpliterator> implements Spliterator { final F fromSpliterator; long index; MapWithIndexSpliterator(F fromSpliterator, long index) { this.fromSpliterator = fromSpliterator; this.index = index; } abstract S createSplit(F from, long i); @Override @CheckForNull public S trySplit() { Spliterator splitOrNull = fromSpliterator.trySplit(); if (splitOrNull == null) { return null; } @SuppressWarnings("unchecked") F split = (F) splitOrNull; S result = createSplit(split, index); this.index += split.getExactSizeIfKnown(); return result; } @Override public long estimateSize() { return fromSpliterator.estimateSize(); } @Override public int characteristics() { return fromSpliterator.characteristics() & (Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED); } } /** * An analogue of {@link java.util.function.IntFunction} also accepting an index. * *

This interface is only intended for use by callers of {@link #mapWithIndex(IntStream, * IntFunctionWithIndex)}. * * @since 21.0 */ public interface IntFunctionWithIndex { /** Applies this function to the given argument and its index within a stream. */ @ParametricNullness R apply(int from, long index); } /** * An analogue of {@link java.util.function.LongFunction} also accepting an index. * *

This interface is only intended for use by callers of {@link #mapWithIndex(LongStream, * LongFunctionWithIndex)}. * * @since 21.0 */ public interface LongFunctionWithIndex { /** Applies this function to the given argument and its index within a stream. */ @ParametricNullness R apply(long from, long index); } /** * An analogue of {@link java.util.function.DoubleFunction} also accepting an index. * *

This interface is only intended for use by callers of {@link #mapWithIndex(DoubleStream, * DoubleFunctionWithIndex)}. * * @since 21.0 */ public interface DoubleFunctionWithIndex { /** Applies this function to the given argument and its index within a stream. */ @ParametricNullness R apply(double from, long index); } /** * Returns the last element of the specified stream, or {@link java.util.Optional#empty} if the * stream is empty. * *

Equivalent to {@code stream.reduce((a, b) -> b)}, but may perform significantly better. This * method's runtime will be between O(log n) and O(n), performing better on efficiently splittable * streams. * *

If the stream has nondeterministic order, this has equivalent semantics to {@link * Stream#findAny} (which you might as well use). * * @see Stream#findFirst() * @throws NullPointerException if the last element of the stream is null */ /* * By declaring instead of , we declare this method as requiring a * stream whose elements are non-null. However, the method goes out of its way to still handle * nulls in the stream. This means that the method can safely be used with a stream that contains * nulls as long as the *last* element is *not* null. * * (To "go out of its way," the method tracks a `set` bit so that it can distinguish "the final * split has a last element of null, so throw NPE" from "the final split was empty, so look for an * element in the prior one.") */ public static java.util.Optional findLast(Stream stream) { class OptionalState { boolean set = false; @CheckForNull T value = null; void set(T value) { this.set = true; this.value = value; } T get() { /* * requireNonNull is safe because we call get() only if we've previously called set(). * * (For further discussion of nullness, see the comment above the method.) */ return requireNonNull(value); } } OptionalState state = new OptionalState(); Deque> splits = new ArrayDeque<>(); splits.addLast(stream.spliterator()); while (!splits.isEmpty()) { Spliterator spliterator = splits.removeLast(); if (spliterator.getExactSizeIfKnown() == 0) { continue; // drop this split } // Many spliterators will have trySplits that are SUBSIZED even if they are not themselves // SUBSIZED. if (spliterator.hasCharacteristics(Spliterator.SUBSIZED)) { // we can drill down to exactly the smallest nonempty spliterator while (true) { Spliterator prefix = spliterator.trySplit(); if (prefix == null || prefix.getExactSizeIfKnown() == 0) { break; } else if (spliterator.getExactSizeIfKnown() == 0) { spliterator = prefix; break; } } // spliterator is known to be nonempty now spliterator.forEachRemaining(state::set); return java.util.Optional.of(state.get()); } Spliterator prefix = spliterator.trySplit(); if (prefix == null || prefix.getExactSizeIfKnown() == 0) { // we can't split this any further spliterator.forEachRemaining(state::set); if (state.set) { return java.util.Optional.of(state.get()); } // fall back to the last split continue; } splits.addLast(prefix); splits.addLast(spliterator); } return java.util.Optional.empty(); } /** * Returns the last element of the specified stream, or {@link OptionalInt#empty} if the stream is * empty. * *

Equivalent to {@code stream.reduce((a, b) -> b)}, but may perform significantly better. This * method's runtime will be between O(log n) and O(n), performing better on efficiently splittable * streams. * * @see IntStream#findFirst() * @throws NullPointerException if the last element of the stream is null */ public static OptionalInt findLast(IntStream stream) { // findLast(Stream) does some allocation, so we might as well box some more java.util.Optional boxedLast = findLast(stream.boxed()); return boxedLast.map(OptionalInt::of).orElseGet(OptionalInt::empty); } /** * Returns the last element of the specified stream, or {@link OptionalLong#empty} if the stream * is empty. * *

Equivalent to {@code stream.reduce((a, b) -> b)}, but may perform significantly better. This * method's runtime will be between O(log n) and O(n), performing better on efficiently splittable * streams. * * @see LongStream#findFirst() * @throws NullPointerException if the last element of the stream is null */ public static OptionalLong findLast(LongStream stream) { // findLast(Stream) does some allocation, so we might as well box some more java.util.Optional boxedLast = findLast(stream.boxed()); return boxedLast.map(OptionalLong::of).orElseGet(OptionalLong::empty); } /** * Returns the last element of the specified stream, or {@link OptionalDouble#empty} if the stream * is empty. * *

Equivalent to {@code stream.reduce((a, b) -> b)}, but may perform significantly better. This * method's runtime will be between O(log n) and O(n), performing better on efficiently splittable * streams. * * @see DoubleStream#findFirst() * @throws NullPointerException if the last element of the stream is null */ public static OptionalDouble findLast(DoubleStream stream) { // findLast(Stream) does some allocation, so we might as well box some more java.util.Optional boxedLast = findLast(stream.boxed()); return boxedLast.map(OptionalDouble::of).orElseGet(OptionalDouble::empty); } private Streams() {} }