View Javadoc
1   /*
2    * *************************************************************************************************************************************************************
3    *
4    * TheseFoolishThings: Miscellaneous utilities
5    * http://tidalwave.it/projects/thesefoolishthings
6    *
7    * Copyright (C) 2009 - 2026 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 javax.annotation.Nonnegative;
29  // import javax.annotation.concurrent.Immutable;
30  // import javax.annotation.concurrent.NotThreadSafe;
31  import jakarta.annotation.Nonnull;
32  import java.util.Map;
33  import java.util.concurrent.atomic.AtomicInteger;
34  import java.util.function.IntFunction;
35  import java.util.function.IntUnaryOperator;
36  import java.util.stream.Collector;
37  import java.util.stream.Collectors;
38  import java.util.stream.IntStream;
39  import java.util.stream.Stream;
40  import java.util.stream.StreamSupport;
41  import lombok.EqualsAndHashCode;
42  import lombok.Getter;
43  import lombok.RequiredArgsConstructor;
44  import lombok.ToString;
45  
46  /***************************************************************************************************************************************************************
47   *
48   * A value object that contains a pair of values. Some factory methods allow creating pairs out of existing collections or arrays associating an index.
49   *
50   * @param   <A>     the type of the former element
51   * @param   <B>     the type of the latter element
52   * @author  Fabrizio Giudici
53   * @since   3.2-ALPHA-6
54   * @it.tidalwave.javadoc.draft
55   *
56   **************************************************************************************************************************************************************/
57  @Getter /* @Immutable */ @RequiredArgsConstructor(staticName = "of") @ToString @EqualsAndHashCode
58  public class Pair<A, B>
59    {
60      /** A base 0 index rebaser. */
61      public static final IntUnaryOperator BASE_0 = i -> i;
62  
63      /** A base 1 index rebaser. */
64      public static final IntUnaryOperator BASE_1 = i -> i + 1;
65  
66      @Nonnull
67      public final A a;
68  
69      @Nonnull
70      public final B b;
71  
72      /***********************************************************************************************************************************************************
73       * {@return a new {@link Stream} of {@code Pair}s composed of a given fixed value and another element taken from another {@link Stream}}.
74       * @param   <T>     the type of the value
75       * @param   <U>     the type of the {@code Stream}
76       * @param   value   the value
77       * @param   stream  the {@code Stream}
78       * @since           3.2-ALPHA-12
79       **********************************************************************************************************************************************************/
80      @Nonnull
81      public static <T, U> Stream<Pair<T, U>> pairStream (@Nonnull final T value, @Nonnull final Stream<? extends U> stream)
82        {
83          return stream.map(object -> Pair.of(value, object));
84        }
85  
86      /***********************************************************************************************************************************************************
87       * {@return a new {@link Stream} of {@code Pair}s composed of a given fixed value and an integer in the given range}.
88       * @param   <T>     the type of the value
89       * @param   value   the value
90       * @param   from    the first value of the integer {@code Stream} (included)
91       * @param   to      the last value of the integer {@code Stream} (excluded)
92       * @return          the {@code Stream} of {@code Pair}s
93       **********************************************************************************************************************************************************/
94      @Nonnull
95      public static <T> Stream<Pair<T, Integer>> pairRange (@Nonnull final T value, /* @Nonnegative */ final int from, /* @Nonnegative */ final int to)
96        {
97          return pairStream(value, IntStream.range(from, to).boxed());
98        }
99  
100     /***********************************************************************************************************************************************************
101      * {@return a new {@link Stream} of {@code Pair}s composed of a given fixed value and an integer in the given range}.
102      * @param   <T>     the type of the value
103      * @param   value   the value
104      * @param   from    the first value of the integer {@code Stream} (included)
105      * @param   to      the last value of the integer {@code Stream} (included)
106      * @since           3.2-ALPHA-12
107      **********************************************************************************************************************************************************/
108     @Nonnull
109     public static <T> Stream<Pair<T, Integer>> pairRangeClosed (@Nonnull final T value, /* @Nonnegative */ final int from, /* @Nonnegative */ final int to)
110       {
111         return pairStream(value, IntStream.rangeClosed(from, to).boxed());
112       }
113 
114     /***********************************************************************************************************************************************************
115      * {@return a new {@link Stream} out of the elements in a given array made of {@link Pair}s {@code (index, value)}}.
116      * @param       <T>             the type of the elements
117      * @param       array           the array
118      * @see         #isEven(Pair)
119      * @see         #isOdd(Pair)
120      **********************************************************************************************************************************************************/
121     @Nonnull
122     public static <T> Stream<Pair<Integer, T>> indexedPairStream (@Nonnull final T[] array)
123       {
124         return indexedPairStream(array, BASE_0);
125       }
126 
127     /***********************************************************************************************************************************************************
128      * {@return a new {@link Stream} out of the elements in the array, made of {@link Pair}s {@code (index, value)}}. The index can be rebased.
129      * @param       <T>               the type of the elements
130      * @param       array             the array
131      * @param       rebaser           the rebaser of the index (BASE_0, BASE_1 or a similar function)
132      * @see         #isEven(Pair)
133      * @see         #isOdd(Pair)
134      **********************************************************************************************************************************************************/
135     @Nonnull
136     public static <T> Stream<Pair<Integer, T>> indexedPairStream (@Nonnull final T[] array, @Nonnull final IntUnaryOperator rebaser)
137       {
138         return indexedPairStream(array, rebaser, i -> i);
139       }
140 
141     /***********************************************************************************************************************************************************
142      * {@return a new {@link Stream} out of the elements in a given array made of {@link Pair}s {@code (index, value)}}. The index is transformed with the given
143      * function.
144      * @param       <I>               the type of the transformed index
145      * @param       <T>               the type of the elements
146      * @param       array             the array
147      * @param       indexTransformer  the transformer of the index
148      **********************************************************************************************************************************************************/
149     @Nonnull
150     public static <I, T> Stream<Pair<I, T>> indexedPairStream (@Nonnull final T[] array, @Nonnull final IntFunction<? extends I> indexTransformer)
151       {
152         return indexedPairStream(array, BASE_0, indexTransformer);
153       }
154 
155     /***********************************************************************************************************************************************************
156      * {@return a new {@link Stream} out of the elements in the array, made of {@link Pair}s {@code (index, value)}}. The index can be rebased and transformed
157      * with specific functions.
158      * @param       <T>               the type of the elements
159      * @param       <I>               the type of the transformed index
160      * @param       array             the array
161      * @param       rebaser           the rebaser of the index (BASE_0, BASE_1 or a similar function)
162      * @param       indexTransformer  the transformer of the index
163      **********************************************************************************************************************************************************/
164     @Nonnull
165     public static <T, I> Stream<Pair<I, T>> indexedPairStream (@Nonnull final T[] array,
166                                                                @Nonnull final IntUnaryOperator rebaser,
167                                                                @Nonnull final IntFunction<? extends I> indexTransformer)
168       {
169         return IntStream.range(0, array.length).mapToObj(i -> of(indexTransformer.apply(rebaser.applyAsInt(i)), array[i]));
170       }
171 
172     /***********************************************************************************************************************************************************
173      * {@return a new {@link Stream} out of the elements in a given {@link Iterable} made of {@link Pair}s {@code (index, value)}}.
174      * @param       <T>               the type of the elements
175      * @param       iterable          the iterable
176      * @see         #isEven(Pair)
177      * @see         #isOdd(Pair)
178      **********************************************************************************************************************************************************/
179     @Nonnull
180     public static <T> Stream<Pair<Integer, T>> indexedPairStream (@Nonnull final Iterable<? extends T> iterable)
181       {
182         return indexedPairStream(iterable, BASE_0);
183       }
184 
185     /***********************************************************************************************************************************************************
186      * {@return a new {@link Stream} out of the elements in a given {@link Iterable} made of {@link Pair}s {@code (index, value)}}. The index can be rebased.
187      * @param       <T>               the type of the elements
188      * @param       iterable          the iterable
189      * @param       rebaser           the rebaser of the index (BASE_0, BASE_1 or a similar function)
190      * @see         #isEven(Pair)
191      * @see         #isOdd(Pair)
192      **********************************************************************************************************************************************************/
193     @Nonnull
194     public static <T> Stream<Pair<Integer, T>> indexedPairStream (@Nonnull final Iterable<? extends T> iterable, @Nonnull final IntUnaryOperator rebaser)
195       {
196         return indexedPairStream(iterable, rebaser, i -> i);
197       }
198 
199     /***********************************************************************************************************************************************************
200      * {@return a new {@link Stream} out of the elements in a given {@link Iterable} made of {@link Pair}s {@code (index, value)}}. The index is transformed
201      * with the given function.
202      * @param       <I>               the type of the transformed index
203      * @param       <T>               the type of the elements
204      * @param       iterable          the iterable
205      * @param       indexTransformer  the transformer of the index
206      **********************************************************************************************************************************************************/
207     @Nonnull
208     public static <I, T> Stream<Pair<I, T>> indexedPairStream (@Nonnull final Iterable<? extends T> iterable,
209                                                                @Nonnull final IntFunction<? extends I> indexTransformer)
210       {
211         return indexedPairStream(iterable, BASE_0, indexTransformer);
212       }
213 
214     /***********************************************************************************************************************************************************
215      * {@return a new {@link Stream} out of the elements returned by an iterable, made of {@link Pair}s {@code (index, value)}}. The index is rebased and
216      * transformed with specific functions.
217      * @param       <T>               the type of the elements
218      * @param       <I>               the type of the transformed index
219      * @param       iterable          the iterable
220      * @param       rebaser           the rebaser of the index (BASE_0, BASE_1 or a similar function)
221      * @param       indexTransformer  the transformer of the index
222      **********************************************************************************************************************************************************/
223     @Nonnull
224     public static <I, T> Stream<Pair<I, T>> indexedPairStream (@Nonnull final Iterable<? extends T> iterable,
225                                                                @Nonnull final IntUnaryOperator rebaser,
226                                                                @Nonnull final IntFunction<? extends I> indexTransformer)
227       {
228         return new Factory<I, T>().stream(iterable, rebaser, indexTransformer);
229       }
230 
231     /***********************************************************************************************************************************************************
232      * {@return a new {@link Stream} out of the elements in a given {@link Stream} made of {@link Pair}s {@code (index, value)}}.
233      * @param       <T>               the type of the elements
234      * @param       stream            the stream
235      * @since       3.2-ALPHA-12
236      **********************************************************************************************************************************************************/
237     @Nonnull @SuppressWarnings("unchecked")
238     public static <T> Stream<Pair<Integer, T>> indexedPairStream (@Nonnull final Stream<? extends T> stream)
239       {
240         return indexedPairStream(((Stream<T>)stream)::iterator);
241       }
242 
243     /***********************************************************************************************************************************************************
244      * {@return a new {@link Stream} out of the elements in a given {@link Stream} made of {@link Pair}s {@code (index, value)}}. The index can be rebased.
245      * @param       <T>               the type of the elements
246      * @param       stream            the stream
247      * @param       rebaser           the rebaser of the index ({@link #BASE_0}, {@link #BASE_1} or a similar function)
248      * @since       3.2-ALPHA-12
249      **********************************************************************************************************************************************************/
250     @Nonnull @SuppressWarnings("unchecked")
251     public static <T> Stream<Pair<Integer, T>> indexedPairStream (@Nonnull final Stream<? extends T> stream, @Nonnull final IntUnaryOperator rebaser)
252       {
253         return indexedPairStream(((Stream<T>)stream)::iterator, rebaser);
254       }
255 
256     /***********************************************************************************************************************************************************
257      * {@return a new {@link Stream} out of the elements in a given {@link Stream} made of {@link Pair}s {@code (index, value)}}. The index is transformed with
258      * the given function.
259      * @param       <I>               the type of the transformed index
260      * @param       <T>               the type of the elements
261      * @param       stream            the stream
262      * @param       indexTransformer  the transformer of the index
263      * @since       3.2-ALPHA-12
264      **********************************************************************************************************************************************************/
265     @SuppressWarnings("unchecked")
266     @Nonnull
267     public static <I, T> Stream<Pair<I, T>> indexedPairStream (@Nonnull final Stream<? extends T> stream,
268                                                                @Nonnull final IntFunction<? extends I> indexTransformer)
269       {
270         return indexedPairStream(((Stream<T>)stream)::iterator, indexTransformer);
271       }
272 
273     /***********************************************************************************************************************************************************
274      * {@return a new {@link Stream} out of the elements returned by a Stream, made of {@link Pair}s {@code (index, value)}}. The index is rebased and
275      * transformed with specific functions.
276      * @param       <T>               the type of the elements
277      * @param       <I>               the type of the transformed index
278      * @param       stream            the stream
279      * @param       rebaser           the rebaser of the index ({@link #BASE_0}, {@link #BASE_1} or a similar function)
280      * @param       indexTransformer  the transformer of the index
281      * @since       3.2-ALPHA-12
282      **********************************************************************************************************************************************************/
283     @SuppressWarnings("unchecked")
284     @Nonnull
285     public static <I, T> Stream<Pair<I, T>> indexedPairStream (@Nonnull final Stream<? extends T> stream,
286                                                                @Nonnull final IntUnaryOperator rebaser,
287                                                                @Nonnull final IntFunction<? extends I> indexTransformer)
288       {
289         return indexedPairStream(((Stream<T>)stream)::iterator, rebaser, indexTransformer);
290       }
291 
292     /***********************************************************************************************************************************************************
293      * {@return a new {@link Stream} out of the elements returned by a supplier, made of {@link Pair}s {@code (index, value)}}.
294      * @param       <T>               the type of the elements
295      * @param       from              the first index (included)
296      * @param       to                the last index (excluded)
297      * @param       valueSupplier     the supplier of values
298      **********************************************************************************************************************************************************/
299     @Nonnull
300     public static <T> Stream<Pair<Integer, T>> indexedPairStream (/* @Nonnegative */ final int from,
301                                                                   /* @Nonnegative */ final int to,
302                                                                   @Nonnull final IntFunction<? extends T> valueSupplier)
303       {
304         return indexedPairStream(from, to, valueSupplier, BASE_0, i -> i);
305       }
306 
307     /***********************************************************************************************************************************************************
308      * {@return a new {@link Stream} out of the elements returned by a supplier, made of {@link Pair}s  {@code (index, value)}}.
309      * @param       <T>               the type of the elements
310      * @param       from              the first index (included)
311      * @param       to                the last index (excluded)
312      * @param       valueSupplier     the supplier of values
313      * @param       rebaser           the rebaser of the index ({@link #BASE_0}, {@link #BASE_1} or a similar function)
314      **********************************************************************************************************************************************************/
315     @Nonnull
316     public static <T> Stream<Pair<Integer, T>> indexedPairStream (/* @Nonnegative */ final int from,
317                                                                   /* @Nonnegative */ final int to,
318                                                                   @Nonnull final IntFunction<? extends T> valueSupplier,
319                                                                   @Nonnull final IntUnaryOperator rebaser)
320       {
321         return indexedPairStream(from, to, valueSupplier, rebaser, i -> i);
322       }
323 
324     /***********************************************************************************************************************************************************
325      * {@return a new {@link Stream} out of the elements returned by a supplier, made of {@link Pair}s  {@code (index, value)}}. The index can be rebased and
326      * transformed with specific functions.
327      * @param       <I>               the type of the transformed index
328      * @param       <T>               the type of the elements
329      * @param       from              the first index (included)
330      * @param       to                the last index (excluded)
331      * @param       valueSupplier     the supplier of values
332      * @param       rebaser           the rebaser of the index ({@link #BASE_0}, {@link #BASE_1} or a similar function)
333      * @param       indexTransformer  the transformer of the index
334      **********************************************************************************************************************************************************/
335     @Nonnull
336     public static <T, I> Stream<Pair<I, T>> indexedPairStream (/* @Nonnegative */ final int from,
337                                                                /* @Nonnegative */ final int to,
338                                                                @Nonnull final IntFunction<? extends T> valueSupplier,
339                                                                @Nonnull final IntUnaryOperator rebaser,
340                                                                @Nonnull final IntFunction<? extends I> indexTransformer)
341       {
342         return IntStream.range(from, to).mapToObj(i -> Pair.of(indexTransformer.apply(rebaser.applyAsInt(i)), valueSupplier.apply(i)));
343       }
344 
345     /***********************************************************************************************************************************************************
346      * {@return a new {@link Collector} that produces a {@link Map} whose key is field {@code a} and value field {@code b}}.
347      * Use with {@link Stream#collect(Collector)}.
348      * @param <A>                     the type of the former element of the pair
349      * @param <B>                     the type of the latter element of the pair
350      * @return                        the {@code Collector}
351      **********************************************************************************************************************************************************/
352     @Nonnull
353     public static <A, B> Collector<Pair<A, B>, ?, Map<A, B>> pairsToMap()
354       {
355         return Collectors.toMap(p -> p.a, p -> p.b);
356       }
357 
358     /***********************************************************************************************************************************************************
359      * {@return a new stream of {@link Pair}s created by “zipping” together two existing streams}.
360      * @param   streamA               the first {@link Stream}
361      * @param   streamB               the second {@link Stream}
362      * @param   <A>                   the type of elements of the first {@link Stream}
363      * @param   <B>                   the type of elements of the second {@link Stream}
364      * @since   3.2-ALPHA-17 (since 3.2-ALPHA-12 was in {@code StreamOperations}
365      **********************************************************************************************************************************************************/
366     @Nonnull
367     public static <A, B> Stream<Pair<A, B>> zip (@Nonnull final Stream<? extends A> streamA, @Nonnull final Stream<? extends B> streamB)
368       {
369         return StreamUtils.zip(streamA, streamB);
370       }
371 
372     /***********************************************************************************************************************************************************
373      * {@return {@code true} if the given pair with an integer member has an even integer}.
374      * @param   pair                  the pair
375      * @since   5.0-ALPHA-3
376      * @see     #indexedPairStream(Object[])
377      **********************************************************************************************************************************************************/
378     public static boolean isEven (@Nonnull final Pair<Integer, ?> pair)
379       {
380         return pair.a % 2 == 0;
381       }
382 
383     /***********************************************************************************************************************************************************
384      * {@return {@code true} if the given pair with an integer member has an odd integer}.
385      * @param   pair                  the pair
386      * @since   5.0-ALPHA-3
387      * @see     #indexedPairStream(Object[])
388      **********************************************************************************************************************************************************/
389     public static boolean isOdd (@Nonnull final Pair<Integer, ?> pair)
390       {
391         return pair.a % 2 != 0;
392       }
393 
394     /***********************************************************************************************************************************************************
395      * A factory for indexed streams.
396      **********************************************************************************************************************************************************/
397     /* @NotThreadSafe */
398     static final class Factory<I, T>
399       {
400         private final AtomicInteger n = new AtomicInteger(0);
401 
402         @Nonnull
403         public Stream<Pair<I, T>> stream (@Nonnull final Iterable<? extends T> iterable,
404                                           @Nonnull final IntUnaryOperator rebaser,
405                                           @Nonnull final IntFunction<? extends I> indexFunction)
406           {
407             return StreamSupport.stream(iterable.spliterator(), false)
408                                 .map(o -> Pair.of(indexFunction.apply(rebaser.applyAsInt(n.getAndIncrement())), o));
409           }
410       }
411   }