View Javadoc
1   /*
2    * *************************************************************************************************************************************************************
3    *
4    * TheseFoolishThings: Miscellaneous utilities
5    * http://tidalwave.it/projects/thesefoolishthings
6    *
7    * Copyright (C) 2009 - 2025 by Tidalwave s.a.s. (http://tidalwave.it)
8    *
9    * *************************************************************************************************************************************************************
10   *
11   * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
12   * You may obtain a copy of the License at
13   *
14   *     http://www.apache.org/licenses/LICENSE-2.0
15   *
16   * 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
17   * CONDITIONS OF ANY KIND, either express or implied.  See the License for the specific language governing permissions and limitations under the License.
18   *
19   * *************************************************************************************************************************************************************
20   *
21   * git clone https://bitbucket.org/tidalwave/thesefoolishthings-src
22   * git clone https://github.com/tidalwave-it/thesefoolishthings-src
23   *
24   * *************************************************************************************************************************************************************
25   */
26  package it.tidalwave.util;
27  
28  import jakarta.annotation.Nonnull;
29  import java.time.Instant;
30  import java.time.LocalDateTime;
31  import java.time.ZoneId;
32  import java.time.ZoneOffset;
33  import java.util.Random;
34  import java.util.Spliterator;
35  import java.util.Spliterators;
36  import java.util.function.BiFunction;
37  import java.util.function.Consumer;
38  import java.util.stream.Stream;
39  import java.util.stream.StreamSupport;
40  import lombok.AccessLevel;
41  import lombok.NoArgsConstructor;
42  
43  /***************************************************************************************************************************************************************
44   *
45   * A collection of operations on {@link Stream}s.
46   *
47   * @author  Fabrizio Giudici
48   * @since   3.2-ALPHA-12
49   * @it.tidalwave.javadoc.draft
50   *
51   **************************************************************************************************************************************************************/
52  @NoArgsConstructor(access = AccessLevel.PRIVATE)
53  public final class StreamUtils
54    {
55      /***********************************************************************************************************************************************************
56       * Zips two streams.
57       *
58       * @param   streamA     the first {@link Stream}
59       * @param   streamB     the second {@link Stream}
60       * @param   <A>         the type of elements of the first {@link Stream}
61       * @param   <B>         the type of elements of the second {@link Stream}
62       * @return              the zipped {@link Stream} of {@link Pair}s
63       * @see     #zip(Stream, Stream, BiFunction)
64       * @since   3.2-ALPHA-20
65       **********************************************************************************************************************************************************/
66      @Nonnull
67      public static <A, B> Stream<Pair<A, B>> zip (@Nonnull final Stream<? extends A> streamA,
68                                                   @Nonnull final Stream<? extends B> streamB)
69        {
70          return zip(streamA, streamB, Pair::of);
71        }
72  
73      /***********************************************************************************************************************************************************
74       * Zips two streams.
75       *
76       * @param   streamA     the first {@link Stream}
77       * @param   streamB     the second {@link Stream}
78       * @param   zipper      the zipping function
79       * @param   <A>         the type of elements of the first {@link Stream}
80       * @param   <B>         the type of elements of the second {@link Stream}
81       * @param   <R>         the type of elements of the zipped {@link Stream}
82       * @return              the zipped {@link Stream}
83       * @see     #zip(Stream, Stream)
84       * @since   3.2-ALPHA-12
85       **********************************************************************************************************************************************************/
86      @Nonnull
87      public static <A, B, R> Stream<R> zip (@Nonnull final Stream<? extends A> streamA,
88                                             @Nonnull final Stream<? extends B> streamB,
89                                             @Nonnull final BiFunction<? super A, ? super B, ? extends R> zipper)
90        {
91          final var parallel = streamA.isParallel() || streamB.isParallel();
92          final var sa = streamA.spliterator();
93          final var sb = streamB.spliterator();
94          final var characteristics =
95                  sa.characteristics() & sb.characteristics() & (Spliterator.SIZED | Spliterator.ORDERED);
96          final var a = Spliterators.iterator(sa);
97          final var b = Spliterators.iterator(sb);
98          final var estSize = Math.min(sa.estimateSize(), sb.estimateSize());
99          return StreamSupport.stream(new Spliterators.AbstractSpliterator<R>(estSize, characteristics)
100             {
101               @Override
102               public boolean tryAdvance (@Nonnull final Consumer<? super R> action)
103                 {
104                   if (a.hasNext() && b.hasNext())
105                     {
106                       action.accept(zipper.apply(a.next(), b.next()));
107                       return true;
108                     }
109 
110                   return false;
111                 }
112             },
113           parallel)
114           .onClose(streamA::close)
115           .onClose(streamB::close);
116       }
117 
118     /***********************************************************************************************************************************************************
119      * Returns a {@code Stream} of random {@link LocalDateTime}s, in the given range.
120      *
121      * @param   seed      the random seed
122      * @param   from      the lower bound of the range (included)
123      * @param   to        the upper bound of the range (excluded)
124      * @return            the stream
125      * @since   3.2-ALPHA-19
126      **********************************************************************************************************************************************************/
127     @Nonnull
128     public static Stream<LocalDateTime> randomLocalDateTimeStream (final long seed,
129                                                                    @Nonnull final LocalDateTime from,
130                                                                    @Nonnull final LocalDateTime to)
131       {
132         final var zo = ZoneOffset.UTC;
133         return new Random(seed)
134                 .longs(from.toEpochSecond(zo), to.toEpochSecond(zo))
135                 .mapToObj(l -> LocalDateTime.ofInstant(Instant.ofEpochSecond(l), ZoneId.of(zo.getId())));
136       }
137   }