guava/src/com/google/common/collect/Streams.java
@SafeVarargs
  public static <T> Stream<T> concat(Stream<? extends T>... 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<Spliterator<? extends T>> splitrsBuilder =
        new ImmutableList.Builder<>(streams.length);
    for (Stream<? extends T> stream : streams) {
      isParallel |= stream.isParallel();
      Spliterator<? extends T> 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<T>) splitr,
                characteristics,
                estimatedSize),
            isParallel)
        .onClose(
            () -> {
              for (Stream<? extends T> stream : streams) {
                stream.close();
              }
            });
  }
@SafeVarargs
  public static <T> Stream<T> concat(Stream<? extends T>... 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<Spliterator<? extends T>> splitrsBuilder =
        new ImmutableList.Builder<>(streams.length);
    for (Stream<? extends T> stream : streams) {
      isParallel |= stream.isParallel();
      Spliterator<? extends T> 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<T>) splitr,
                characteristics,
                estimatedSize),
            isParallel)
        .onClose(
            () -> {
              for (Stream<? extends T> stream : streams) {
                stream.close();
              }
            });
  }
@ SafeVarargs public static < T > Stream < T > concat ( Stream < ? extends T > ... 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 < Spliterator < ? extends T > > splitrsBuilder = new ImmutableList . Builder <> ( streams . length ) ; for ( Stream < ? extends T > stream : streams ) { isParallel |= stream . isParallel ( ) ; Spliterator < ? extends T > 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 < T > ) splitr , characteristics , estimatedSize ) , isParallel ) . onClose ( ( ) -> { for ( Stream < ? extends T > stream : streams ) { stream . close ( ) ; } } ) ; }
Returns a {@link Stream} containing the elements of the first stream, followed by the elements
of the second stream, and so on.

<p>This is equivalent to {@code Stream.of(streams).flatMap(stream -> stream)}, but the returned
stream may perform better.

@see Stream#concat(Stream, Stream)
Returns a { @link Stream } containing the elements of the first stream followed by the elements of the second stream and so on .