Content of file StreamUtils.java

/*
 * *********************************************************************************************************************
 *
 * TheseFoolishThings: Miscellaneous utilities
 * http://tidalwave.it/projects/thesefoolishthings
 *
 * Copyright (C) 2009 - 2023 by Tidalwave s.a.s. (http://tidalwave.it)
 *
 * *********************************************************************************************************************
 *
 * 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.
 *
 * *********************************************************************************************************************
 *
 * git clone https://bitbucket.org/tidalwave/thesefoolishthings-src
 * git clone https://github.com/tidalwave-it/thesefoolishthings-src
 *
 * *********************************************************************************************************************
 */
package it.tidalwave.util;

import javax.annotation.Nonnull;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.Random;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;

/***********************************************************************************************************************
 *
 * A collection of operations on {@link Stream}s.
 *
 * @author  Fabrizio Giudici
 * @since   3.2-ALPHA-12
 * @it.tidalwave.javadoc.draft
 *
 **********************************************************************************************************************/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class StreamUtils
  {
    /*******************************************************************************************************************
     *
     * Zips two streams.
     *
     * @param   streamA     the first {@link Stream}
     * @param   streamB     the second {@link Stream}
     * @param   <A>         the type of elements of the first {@link Stream}
     * @param   <B>         the type of elements of the second {@link Stream}
     * @return              the zipped {@link Stream} of {@link Pair}s
     * @see     #zip(Stream, Stream, BiFunction)
     * @since   3.2-ALPHA-20
     *
     ******************************************************************************************************************/
    @Nonnull
    public static <A, B> Stream<Pair<A, B>> zip (@Nonnull final Stream<? extends A> streamA,
                                                 @Nonnull final Stream<? extends B> streamB)
      {
        return zip(streamA, streamB, Pair::of);
      }

    /*******************************************************************************************************************
     *
     * Zips two streams.
     *
     * @param   streamA     the first {@link Stream}
     * @param   streamB     the second {@link Stream}
     * @param   zipper      the zipping function
     * @param   <A>         the type of elements of the first {@link Stream}
     * @param   <B>         the type of elements of the second {@link Stream}
     * @param   <R>         the type of elements of the zipped {@link Stream}
     * @return              the zipped {@link Stream}
     * @see     #zip(Stream, Stream)
     * @since   3.2-ALPHA-12
     *
     ******************************************************************************************************************/
    @Nonnull
    public static <A, B, R> Stream<R> zip (@Nonnull final Stream<? extends A> streamA,
                                           @Nonnull final Stream<? extends B> streamB,
                                           @Nonnull final BiFunction<? super A, ? super B, ? extends R> zipper)
      {
        final var parallel = streamA.isParallel() || streamB.isParallel();
        final var sa = streamA.spliterator();
        final var sb = streamB.spliterator();
        final var characteristics =
                sa.characteristics() & sb.characteristics() & (Spliterator.SIZED | Spliterator.ORDERED);
        final var a = Spliterators.iterator(sa);
        final var b = Spliterators.iterator(sb);
        final var estSize = Math.min(sa.estimateSize(), sb.estimateSize());
        return StreamSupport.stream(new Spliterators.AbstractSpliterator<R>(estSize, characteristics)
            {
              @Override
              public boolean tryAdvance (@Nonnull final Consumer<? super R> action)
                {
                  if (a.hasNext() && b.hasNext())
                    {
                      action.accept(zipper.apply(a.next(), b.next()));
                      return true;
                    }

                  return false;
                }
            },
          parallel)
          .onClose(streamA::close)
          .onClose(streamB::close);
      }

    /*******************************************************************************************************************
     *
     * Returns a {@code Stream} of random {@link LocalDateTime}s, in the given range.
     *
     * @param   seed      the random seed
     * @param   from      the lower bound of the range (included)
     * @param   to        the upper bound of the range (excluded)
     * @return            the stream
     * @since   3.2-ALPHA-19
     *
     ******************************************************************************************************************/
    @Nonnull
    public static Stream<LocalDateTime> randomLocalDateTimeStream (final long seed,
                                                                   @Nonnull final LocalDateTime from,
                                                                   @Nonnull final LocalDateTime to)
      {
        final var zo = ZoneOffset.UTC;
        return new Random(seed)
                .longs(from.toEpochSecond(zo), to.toEpochSecond(zo))
Random object created and used only once in it.tidalwave.util.StreamUtils.randomLocalDateTimeStream(long, LocalDateTime, LocalDateTime)

This code creates a java.util.Random object, uses it to generate one random number, and then discards the Random object. This produces mediocre quality random numbers and is inefficient. If possible, rewrite the code so that the Random object is created once and saved, and each time a new random number is required invoke a method on the existing Random object to obtain it.

If it is important that the generated Random numbers not be guessable, you must not create a new Random for each random number; the values are too easily guessable. You should strongly consider using a java.security.SecureRandom instead (and avoid allocating a new SecureRandom for each random number needed).

.mapToObj(l -> LocalDateTime.ofInstant(Instant.ofEpochSecond(l), ZoneId.of(zo.getId()))); } }