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 }