001/* 002 * Java Genetic Algorithm Library (jenetics-9.1.0). 003 * Copyright (c) 2007-2026 Franz Wilhelmstötter 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 * 017 * Author: 018 * Franz Wilhelmstötter (franz.wilhelmstoetter@gmail.com) 019 */ 020package io.jenetics.util; 021 022import static java.time.Clock.systemUTC; 023import static java.util.Objects.requireNonNull; 024 025import java.time.Clock; 026import java.time.Duration; 027import java.util.Comparator; 028import java.util.concurrent.atomic.AtomicInteger; 029import java.util.concurrent.atomic.AtomicLong; 030import java.util.concurrent.atomic.AtomicReference; 031import java.util.function.BinaryOperator; 032import java.util.function.Function; 033import java.util.stream.Gatherer; 034import java.util.stream.Stream; 035 036/** 037 * This class contains factory methods for (flat) mapping stream element. The 038 * functions of this class can be used in the following way. 039 * {@snippet lang = "java": 040 * final ISeq<Integer> values = new Random().ints(0, 100).boxed() 041 * .limit(100) 042 * .gather(Streams.maxOfInterval(13)) 043 * .collect(ISeq.toISeq()); 044 *} 045 * 046 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a> 047 * @since 6.0 048 * @version 6.0 049 */ 050public final class Streams { 051 private Streams() { 052 } 053 054 055 /** 056 * Return a new flat-mapper function, which guarantees a strictly increasing 057 * stream, from an arbitrarily ordered source stream. Note that this 058 * function doesn't sort the stream. It <em>just</em> skips the <em>out of 059 * order</em> elements. 060 * 061 * <pre>{@code 062 * +----3--2--5--4--7--7--4--9----| 063 * toStrictlyIncreasing() 064 * +----3-----5-----7--------9----| 065 * }</pre> 066 * 067 * {@snippet lang="java": 068 * @SuppressWarnings("removal") 069 * final ISeq<Integer> values = new Random().ints(0, 100) 070 * .boxed() 071 * .limit(100) 072 * .flatMap(Streams.toStrictlyIncreasing()) 073 * .collect(ISeq.toISeq()); 074 * 075 * System.out.println(values); 076 * // [6,47,65,78,96,96,99] 077 * } 078 * 079 * 080 * @param <C> the comparable type 081 * @return a new flat-mapper function 082 * 083 * @deprecated Use the {@link #strictlyIncreasing()} method instead. 084 */ 085 @Deprecated(forRemoval = true, since = "9.1") 086 public static <C extends Comparable<? super C>> 087 Function<C, Stream<C>> toStrictlyIncreasing() { 088 return strictlyImproving0(Streams::max); 089 } 090 091 /** 092 * Return a new gatherer, which guarantees a strictly increasing stream, from 093 * an arbitrarily ordered source stream. Note that this gatherer doesn't sort 094 * the stream. It <em>just</em> skips the <em>out of order</em> elements. 095 * 096 * <pre>{@code 097 * +----3--2--5--4--7--7--4--9----| 098 * toStrictlyIncreasing() 099 * +----3-----5-----7--------9----| 100 * }</pre> 101 * 102 * {@snippet lang = "java": 103 * final ISeq<Integer> values = new Random().ints(0, 100) 104 * .boxed() 105 * .limit(100) 106 * .gather(Streams.strictlyIncreasing()) 107 * .collect(ISeq.toISeq()); 108 * 109 * System.out.println(values); 110 * // [6,47,65,78,96,96,99] 111 *} 112 * 113 * @param <C> the comparable type 114 * @return a new gatherer 115 */ 116 public static <C extends Comparable<? super C>> 117 Gatherer<C, ?, C> strictlyIncreasing() { 118 return strictlyImproving(Comparator.naturalOrder()); 119 } 120 121 /** 122 * Return a new flat-mapper function, which guarantees a strictly decreasing 123 * stream, from an arbitrarily ordered source stream. Note that this 124 * function doesn't sort the stream. It <em>just</em> skips the <em>out of 125 * order</em> elements. 126 * 127 * <pre>{@code 128 * +----9--8--9--5--6--6--2--9----| 129 * toStrictlyDecreasing() 130 * +----9--8-----5--------2-------| 131 * }</pre> 132 * 133 * {@snippet lang="java": 134 * @SuppressWarnings("removal") 135 * final ISeq<Integer> values = new Random().ints(0, 100) 136 * .boxed() 137 * .limit(100) 138 * .flatMap(Streams.toStrictlyDecreasing()) 139 * .collect(ISeq.toISeq()); 140 * 141 * System.out.println(values); 142 * // [45,32,15,12,3,1] 143 * } 144 * 145 * @param <C> the comparable type 146 * @return a new flat-mapper function 147 * @deprecated Use {@link #strictlyDecreasing()} instead. 148 */ 149 @Deprecated(forRemoval = true, since = "9.1") 150 public static <C extends Comparable<? super C>> 151 Function<C, Stream<C>> toStrictlyDecreasing() { 152 return strictlyImproving0(Streams::min); 153 } 154 155 /** 156 * Return a new gatherer, which guarantees a strictly decreasin stream, from 157 * an arbitrarily ordered source stream. Note that this gatherer doesn't sort 158 * the stream. It <em>just</em> skips the <em>out of order</em> elements. 159 * 160 * <pre>{@code 161 * +----9--8--9--5--6--6--2--9----| 162 * strictlyDecreasing() 163 * +----9--8-----5--------2-------| 164 * }</pre> 165 * 166 * {@snippet lang="java": 167 * final ISeq<Integer> values = new Random().ints(0, 100) 168 * .boxed() 169 * .limit(100) 170 * .gather(Streams.strictlyDecreasing()) 171 * .collect(ISeq.toISeq()); 172 * 173 * System.out.println(values); 174 * // [45,32,15,12,3,1] 175 * } 176 * 177 * @param <C> the comparable type 178 * @return a new flat-mapper function 179 */ 180 public static <C extends Comparable<? super C>> 181 Gatherer<C, ?, C> strictlyDecreasing() { 182 return strictlyImproving(Comparator.<C>naturalOrder().reversed()); 183 } 184 185 /** 186 * Return a new flat-mapper function, which guarantees a strictly improving 187 * stream, from an arbitrarily ordered source stream. Note that this 188 * function doesn't sort the stream. It <em>just</em> skips the <em>out of 189 * order</em> elements. 190 * {@snippet lang="java": 191 * @SuppressWarnings("removal") 192 * final ISeq<Integer> values = new Random().ints(0, 100) 193 * .boxed() 194 * .limit(100) 195 * .flatMap(Streams.toStrictlyImproving(Comparator.naturalOrder())) 196 * .collect(ISeq.toISeq()); 197 * 198 * System.out.println(values); 199 * // [6,47,65,78,96,96,99] 200 * } 201 * 202 * @see #toStrictlyIncreasing() 203 * @see #toStrictlyDecreasing() 204 * 205 * @param <T> the element type 206 * @param comparator the comparator used for testing the elements 207 * @return a new flat-mapper function 208 * @deprecated Use {@link #strictlyImproving(Comparator)} instead. 209 */ 210 @Deprecated(forRemoval = true, since = "9.1") 211 public static <T> Function<T, Stream<T>> 212 toStrictlyImproving(final Comparator<? super T> comparator) { 213 return strictlyImproving0((a, b) -> best(comparator, a, b)); 214 } 215 216 private static <C> Function<C, Stream<C>> 217 strictlyImproving0(final BinaryOperator<C> comparator) { 218 requireNonNull(comparator); 219 220 return new Function<>() { 221 private C _best; 222 223 @Override 224 public Stream<C> apply(final C result) { 225 final C best = comparator.apply(_best, result); 226 227 final Stream<C> stream = best == _best 228 ? Stream.empty() 229 : Stream.of(best); 230 231 _best = best; 232 233 return stream; 234 } 235 }; 236 } 237 238 /** 239 * Return a new gatherer, which guarantees a strictly improving stream, from 240 * an arbitrarily ordered source stream. Note that this gatherer doesn't sort 241 * the stream. It <em>just</em> skips the <em>out of order</em> elements. 242 * {@snippet lang = "java": 243 * final ISeq<Integer> values = new Random().ints(0, 100) 244 * .boxed() 245 * .limit(100) 246 * .gather(Streams.strictlyImproving(Comparator.naturalOrder())) 247 * .collect(ISeq.toISeq()); 248 * 249 * System.out.println(values); 250 * // [6,47,65,78,96,96,99] 251 *} 252 * 253 * @param <T> the element type 254 * @param comparator the comparator used for testing the elements 255 * @return a new gatherer 256 * @throws NullPointerException if the given {@code comparator} is 257 * {@code null} 258 */ 259 public static <T> Gatherer<T, ?, T> 260 strictlyImproving(final Comparator<? super T> comparator) { 261 requireNonNull(comparator); 262 263 return Gatherer.ofSequential( 264 AtomicReference<T>::new, 265 (state, value, downstream) -> { 266 final T best = best(comparator, state.get(), value); 267 if (best != state.get()) { 268 state.set(best); 269 return downstream.push(best); 270 } 271 return true; 272 } 273 ); 274 } 275 276 private static <T extends Comparable<? super T>> T max(final T a, final T b) { 277 return best(Comparator.naturalOrder(), a, b); 278 } 279 280 private static <T extends Comparable<? super T>> T min(final T a, final T b) { 281 return best(Comparator.reverseOrder(), a, b); 282 } 283 284 private static <T> 285 T best(final Comparator<? super T> comparator, final T a, final T b) { 286 if (a == null && b == null) return null; 287 if (a == null) return b; 288 if (b == null) return a; 289 return comparator.compare(a, b) >= 0 ? a : b; 290 } 291 292 /** 293 * Return a new flat-mapper function which returns (emits) the maximal value 294 * of the last <em>n</em> elements. 295 * 296 * <pre>{@code 297 * +----3---+----3---+ 298 * | | | 299 * +----9--8--3--3--5--4--2--9----| 300 * toIntervalMax(3) 301 * +----------9--------5----------| 302 * }</pre> 303 * 304 * @param size the size of the slice 305 * @param <C> the element type 306 * @return a new flat-mapper function 307 * @throws IllegalArgumentException if the given size is smaller than one 308 * @deprecated Will be removed. Use {@link #maxOfInterval(int)} 309 * instead. 310 */ 311 @Deprecated(forRemoval = true, since = "9.1") 312 public static <C extends Comparable<? super C>> 313 Function<C, Stream<C>> toIntervalMax(final int size) { 314 return sliceBest1(Streams::max, size); 315 } 316 317 /** 318 * Return a new flat-mapper function which returns (emits) the minimal value 319 * of the last <em>n</em> elements. 320 * 321 * <pre>{@code 322 * +----3---+----3---+ 323 * | | | 324 * +----9--8--3--3--1--4--2--9----| 325 * toIntervalMin(3) 326 * +----------3--------1----------| 327 * }</pre> 328 * 329 * @param size the size of the slice 330 * @param <C> the element type 331 * @return a new flat-mapper function 332 * @throws IllegalArgumentException if the given size is smaller than one 333 * @deprecated Will be removed. Use {@link #minOfInterval(int)} 334 * instead. 335 */ 336 @Deprecated(forRemoval = true, since = "9.1") 337 public static <C extends Comparable<? super C>> 338 Function<C, Stream<C>> toIntervalMin(final int size) { 339 return sliceBest1(Streams::min, size); 340 } 341 342 /** 343 * Return a new flat-mapper function which returns (emits) the minimal value 344 * of the last <em>n</em> elements. 345 * 346 * @see #toIntervalMax(int) 347 * @see #toIntervalMin(int) 348 * 349 * @param <C> the element type 350 * @param size the size of the slice 351 * @param comparator the comparator used for testing the elements 352 * @return a new flat-mapper function 353 * @throws IllegalArgumentException if the given size is smaller than one 354 * @throws NullPointerException if the given {@code comparator} is 355 * {@code null} 356 * @deprecated Will be removed. Use {@link #bestOfInterval(Comparator, int)} 357 * instead. 358 */ 359 @Deprecated(forRemoval = true, since = "9.1") 360 public static <C> Function<C, Stream<C>> 361 toIntervalBest(final Comparator<? super C> comparator, final int size) { 362 requireNonNull(comparator); 363 return sliceBest1((a, b) -> best(comparator, a, b), size); 364 } 365 366 @Deprecated(forRemoval = true, since = "9.1") 367 private static <C> Function<C, Stream<C>> sliceBest1( 368 final BinaryOperator<C> comp, 369 final int rangeSize 370 ) { 371 requireNonNull(comp); 372 if (rangeSize < 1) { 373 throw new IllegalArgumentException( 374 "Range size must be at least one: " + rangeSize 375 ); 376 } 377 378 return new Function<>() { 379 private int _count = 0; 380 private C _best; 381 382 @Override 383 public Stream<C> apply(final C value) { 384 ++_count; 385 _best = comp.apply(_best, value); 386 387 final Stream<C> result; 388 if (_count >= rangeSize) { 389 result = Stream.of(_best); 390 _count = 0; 391 _best = null; 392 } else { 393 result = Stream.empty(); 394 } 395 396 return result; 397 } 398 }; 399 } 400 401 /** 402 * Return a new gatherer which emits the maximum element of the last <em>n</em> 403 * consumed elements. If the stream ends with a final interval smaller than 404 * {@code n}, the maximum element of this remaining interval is emitted as 405 * well. 406 * 407 * <pre>{@code 408 * +----3---+----3---+ 409 * | | |--+ 410 * +----9--8--3--3--5--4--2--9----| 411 * maxOfInterval(3) 412 * +----------9--------5-----9----| 413 * }</pre> 414 * 415 * @param size the size of the slice 416 * @param <C> the element type 417 * @return a new gatherer 418 * @throws IllegalArgumentException if the given size is smaller than one 419 */ 420 public static <C extends Comparable<? super C>> 421 Gatherer<C, ?, C> maxOfInterval(final int size) { 422 return sliceBestGatherer(Streams::max, size); 423 } 424 425 /** 426 * Return a new gatherer which emits the minimum element of the last <em>n</em> 427 * consumed elements. If the stream ends with a final interval smaller than 428 * {@code n}, the minimum element of this remaining interval is emitted as 429 * well. 430 * 431 * <pre>{@code 432 * +----3---+----3---+ 433 * | | |--+ 434 * +----9--8--3--3--1--4--2--9----| 435 * minOfInterval(3) 436 * +----------3--------1-----2----| 437 * }</pre> 438 * 439 * @param size the size of the slice 440 * @param <C> the element type 441 * @return a new gatherer 442 * @throws IllegalArgumentException if the given size is smaller than one 443 */ 444 public static <C extends Comparable<? super C>> 445 Gatherer<C, ?, C> minOfInterval(final int size) { 446 return sliceBestGatherer(Streams::min, size); 447 } 448 449 /** 450 * Return a new gatherer which emits the best element of the last <em>n</em> 451 * consumed elements. If the stream ends with a final interval smaller than 452 * {@code n}, the best element of this remaining interval is emitted as well. 453 * 454 * @see #maxOfInterval(int) 455 * @see #minOfInterval(int) 456 * 457 * @param <C> the element type 458 * @param size the size of the slice 459 * @param comparator the comparator used for testing the elements 460 * @return a new gatherer 461 * @throws IllegalArgumentException if the given size is smaller than one 462 * @throws NullPointerException if the given {@code comparator} is 463 * {@code null} 464 */ 465 public static <C> Gatherer<C, ?, C> 466 bestOfInterval(final Comparator<? super C> comparator, final int size) { 467 requireNonNull(comparator); 468 return sliceBestGatherer((a, b) -> best(comparator, a, b), size); 469 } 470 471 private static <C> Gatherer<C, ?, C> sliceBestGatherer( 472 final BinaryOperator<C> comp, 473 final int rangeSize 474 ) { 475 requireNonNull(comp); 476 if (rangeSize < 1) { 477 throw new IllegalArgumentException( 478 "Range size must be at least one: " + rangeSize 479 ); 480 } 481 482 record State<C>(AtomicInteger count, AtomicReference<C> best) { 483 State() { 484 this(new AtomicInteger(), new AtomicReference<>()); 485 } 486 } 487 488 return Gatherer.ofSequential( 489 State<C>::new, 490 (state, value, downstream) -> { 491 final var count = state.count.incrementAndGet(); 492 state.best.set(comp.apply(state.best.get(), value)); 493 494 if (count >= rangeSize) { 495 downstream.push(state.best.get()); 496 state.count.set(0); 497 state.best.set(null); 498 } 499 500 return true; 501 }, 502 (state, downstream) -> { 503 if (state.count.get() > 0) { 504 downstream.push(state.best.get()); 505 } 506 } 507 ); 508 509 } 510 511 /** 512 * Return a new flat-mapper function which returns (emits) the maximal value 513 * of the elements emitted within the given {@code timespan}. 514 * 515 * <pre>{@code 516 * +---3s---+---3s---+ 517 * | | | 518 * +----9--8--3--3--5--4--2--9----| 519 * toIntervalMax(3s) 520 * +----------9--------5----------| 521 * }</pre> 522 * 523 * @see #toIntervalMax(Duration, Clock) 524 * 525 * @param <C> the element type 526 * @param timespan the timespan the elements are collected for the 527 * calculation slice 528 * @return a new flat-mapper function 529 * @throws IllegalArgumentException if the given size is smaller than one 530 * @throws NullPointerException if the given {@code timespan} is {@code null} 531 * @deprecated Will be removed. Use {@link #maxOfInterval(Duration)} 532 * instead. 533 */ 534 @Deprecated(forRemoval = true, since = "9.1") 535 public static <C extends Comparable<? super C>> 536 Function<C, Stream<C>> toIntervalMax(final Duration timespan) { 537 return sliceBest0(Streams::max, timespan, systemUTC()); 538 } 539 540 /** 541 * Return a new flat-mapper function which returns (emits) the maximal value 542 * of the elements emitted within the given {@code timespan}. 543 * 544 * <pre>{@code 545 * +---3s---+---3s---+ 546 * | | | 547 * +----9--8--3--3--5--4--2--9----| 548 * toIntervalMax(3s) 549 * +----------9--------5----------| 550 * }</pre> 551 * 552 * @see #toIntervalMax(Duration) 553 * 554 * @param <C> the element type 555 * @param timespan the timespan the elements are collected for the 556 * calculation slice 557 * @param clock the {@code clock} used for measuring the {@code timespan} 558 * @return a new flat-mapper function 559 * @throws IllegalArgumentException if the given size is smaller than one 560 * @throws NullPointerException if one of the arguments is {@code null} 561 * @deprecated Will be removed. Use {@link #maxOfInterval(Duration, Clock)} 562 * instead. 563 */ 564 @Deprecated(forRemoval = true, since = "9.1") 565 public static <C extends Comparable<? super C>> 566 Function<C, Stream<C>> toIntervalMax(final Duration timespan, final Clock clock) { 567 return sliceBest0(Streams::max, timespan, clock); 568 } 569 570 /** 571 * Return a new flat-mapper function which returns (emits) the minimal value 572 * of the elements emitted within the given {@code timespan}. 573 * 574 * <pre>{@code 575 * +---3s---+---3s---+ 576 * | | | 577 * +----9--8--3--3--1--4--2--9----| 578 * toIntervalMin(3s) 579 * +----------3--------1----------| 580 * }</pre> 581 * 582 * @see #toIntervalMin(Duration, Clock) 583 * 584 * @param <C> the element type 585 * @param timespan the timespan the elements are collected for the 586 * calculation slice 587 * @return a new flat-mapper function 588 * @throws IllegalArgumentException if the given size is smaller than one 589 * @throws NullPointerException if the given {@code timespan} is {@code null} 590 * @deprecated Will be removed. Use {@link #minOfInterval(Duration)} 591 * instead. 592 */ 593 @Deprecated(forRemoval = true, since = "9.1") 594 public static <C extends Comparable<? super C>> 595 Function<C, Stream<C>> toIntervalMin(final Duration timespan) { 596 return sliceBest0(Streams::min, timespan, systemUTC()); 597 } 598 599 /** 600 * Return a new flat-mapper function which returns (emits) the minimal value 601 * of the elements emitted within the given {@code timespan}. 602 * 603 * <pre>{@code 604 * +---3s---+---3s---+ 605 * | | | 606 * +----9--8--3--3--1--4--2--9----| 607 * toIntervalMin(3s) 608 * +----------3--------1----------| 609 * }</pre> 610 * 611 * @see #toIntervalMin(Duration) 612 * 613 * @param <C> the element type 614 * @param timespan the timespan the elements are collected for the 615 * calculation slice 616 * @param clock the {@code clock} used for measuring the {@code timespan} 617 * @return a new flat-mapper function 618 * @throws IllegalArgumentException if the given size is smaller than one 619 * @throws NullPointerException if one of the arguments is {@code null} 620 * @deprecated Will be removed. Use {@link #minOfInterval(Duration, Clock)} 621 * instead. 622 */ 623 @Deprecated(forRemoval = true, since = "9.1") 624 public static <C extends Comparable<? super C>> 625 Function<C, Stream<C>> toIntervalMin(final Duration timespan, final Clock clock) { 626 return sliceBest0(Streams::min, timespan, clock); 627 } 628 629 /** 630 * Return a new flat-mapper function which returns (emits) the minimal value 631 * of the elements emitted within the given {@code timespan}. 632 * 633 * @see #toIntervalMin(Duration) 634 * @see #toIntervalMax(Duration) 635 * 636 * @param <C> the element type 637 * @param comparator the comparator used for testing the elements 638 * @param timespan the timespan the elements are collected for the 639 * calculation slice 640 * @return a new flat-mapper function 641 * @throws IllegalArgumentException if the given size is smaller than one 642 * @throws NullPointerException if one of the arguments is {@code null} 643 * @deprecated Will be removed. Use {@link #bestOfInterval(Comparator, Duration)} 644 * instead. 645 */ 646 @Deprecated(forRemoval = true, since = "9.1") 647 public static <C> Function<C, Stream<C>> 648 toIntervalBest(final Comparator<? super C> comparator, final Duration timespan) { 649 requireNonNull(comparator); 650 return sliceBest0((a, b) -> best(comparator, a, b), timespan, systemUTC()); 651 } 652 653 /** 654 * Return a new flat-mapper function which returns (emits) the minimal value 655 * of the elements emitted within the given {@code timespan}. 656 * 657 * @param <C> the element type 658 * @param comparator the comparator used for testing the elements 659 * @param timespan the timespan the elements are collected for the 660 * calculation slice 661 * @param clock the {@code clock} used for measuring the {@code timespan} 662 * @return a new flat-mapper function 663 * @throws IllegalArgumentException if the given size is smaller than one 664 * @throws NullPointerException if one of the arguments is {@code null} 665 * @deprecated Will be removed. Use {@link #bestOfInterval(Comparator, Duration, Clock)} 666 * instead. 667 */ 668 @Deprecated(forRemoval = true, since = "9.1") 669 public static <C> Function<C, Stream<C>> 670 toIntervalBest( 671 final Comparator<? super C> comparator, 672 final Duration timespan, 673 final Clock clock 674 ) { 675 requireNonNull(comparator); 676 return sliceBest0((a, b) -> best(comparator, a, b), timespan, clock); 677 } 678 679 @Deprecated(forRemoval = true, since = "9.1") 680 private static <C> Function<C, Stream<C>> sliceBest0( 681 final BinaryOperator<C> comp, 682 final Duration timespan, 683 final Clock clock 684 ) { 685 requireNonNull(comp); 686 requireNonNull(timespan); 687 requireNonNull(clock); 688 689 return new Function<>() { 690 private final long _timespan = timespan.toMillis(); 691 692 private long _start = 0; 693 private C _best; 694 695 @Override 696 public Stream<C> apply(final C value) { 697 if (_start == 0) { 698 _start = clock.millis(); 699 } 700 701 _best = comp.apply(_best, value); 702 long end = clock.millis(); 703 704 final Stream<C> result; 705 if (end - _start >= _timespan) { 706 result = Stream.of(_best); 707 _start = 0; 708 _best = null; 709 } else { 710 result = Stream.empty(); 711 } 712 713 return result; 714 } 715 }; 716 } 717 718 /** 719 * Return a new gatherer which emits the maximal element consumed within the 720 * given {@code timespan}. 721 * 722 * <pre>{@code 723 * +---3s---+---3s---+ 724 * | | | 725 * +----9--8--3--3--5--4--2--9----| 726 * maxOfInterval(3s) 727 * +----------9--------5----------| 728 * }</pre> 729 * 730 * @see #maxOfInterval(Duration, Clock) 731 * 732 * @param <C> the element type 733 * @param timespan the timespan the elements are collected for the 734 * calculation slice 735 * @return a new gatherer 736 * @throws IllegalArgumentException if the given size is smaller than one 737 * @throws NullPointerException if the given {@code timespan} is {@code null} 738 */ 739 public static <C extends Comparable<? super C>> 740 Gatherer<C, ?, C> maxOfInterval(final Duration timespan) { 741 return sliceBestGatherer(Streams::max, timespan, systemUTC()); 742 } 743 744 /** 745 * Return a new gatherer which emits the maximal element consumed within the 746 * given {@code timespan}. 747 * 748 * <pre>{@code 749 * +---3s---+---3s---+ 750 * | | | 751 * +----9--8--3--3--5--4--2--9----| 752 * maxOfInterval(3s) 753 * +----------9--------5----------| 754 * }</pre> 755 * 756 * @see #maxOfInterval(Duration) 757 * 758 * @param <C> the element type 759 * @param timespan the timespan the elements are collected for the 760 * calculation slice 761 * @param clock the {@code clock} used for measuring the {@code timespan} 762 * @return a new gatherer 763 * @throws IllegalArgumentException if the given size is smaller than one 764 * @throws NullPointerException if one of the arguments is {@code null} 765 */ 766 public static <C extends Comparable<? super C>> 767 Gatherer<C, ?, C> maxOfInterval(final Duration timespan, final Clock clock) { 768 return sliceBestGatherer(Streams::max, timespan, clock); 769 } 770 771 /** 772 * Return a new gatherer which emits the minimal element consumed within the 773 * given {@code timespan}. 774 * 775 * <pre>{@code 776 * +---3s---+---3s---+ 777 * | | | 778 * +----9--8--3--3--1--4--2--9----| 779 * minOfInterval(3s) 780 * +----------3--------1----------| 781 * }</pre> 782 * 783 * @see #minOfInterval(Duration, Clock) 784 * 785 * @param <C> the element type 786 * @param timespan the timespan the elements are collected for the 787 * calculation slice 788 * @return a new gatherer 789 * @throws IllegalArgumentException if the given size is smaller than one 790 * @throws NullPointerException if the given {@code timespan} is {@code null} 791 */ 792 public static <C extends Comparable<? super C>> 793 Gatherer<C, ?, C> minOfInterval(final Duration timespan) { 794 return sliceBestGatherer(Streams::min, timespan, systemUTC()); 795 } 796 797 /** 798 * Return a new gatherer which emits the minimal element consumed within the 799 * given {@code timespan}. 800 * 801 * <pre>{@code 802 * +---3s---+---3s---+ 803 * | | | 804 * +----9--8--3--3--1--4--2--9----| 805 * minOfInterval(3s) 806 * +----------3--------1----------| 807 * }</pre> 808 * 809 * @see #minOfInterval(Duration) 810 * 811 * @param <C> the element type 812 * @param timespan the timespan the elements are collected for the 813 * calculation slice 814 * @param clock the {@code clock} used for measuring the {@code timespan} 815 * @return a new gatherer 816 * @throws IllegalArgumentException if the given size is smaller than one 817 * @throws NullPointerException if one of the arguments is {@code null} 818 */ 819 public static <C extends Comparable<? super C>> 820 Gatherer<C, ?, C> minOfInterval(final Duration timespan, final Clock clock) { 821 return sliceBestGatherer(Streams::min, timespan, clock); 822 } 823 824 /** 825 * Return a new gatherer which emits the <em>best</em> element consumed 826 * within the given {@code timespan}. 827 * 828 * @see #minOfInterval(Duration) 829 * @see #maxOfInterval(Duration) 830 * 831 * @param <C> the element type 832 * @param comparator the comparator used for testing the elements 833 * @param timespan the timespan the elements are collected for the 834 * calculation slice 835 * @return a new gatherer 836 * @throws IllegalArgumentException if the given size is smaller than one 837 * @throws NullPointerException if one of the arguments is {@code null} 838 */ 839 public static <C>Gatherer<C, ?, C> 840 bestOfInterval(final Comparator<? super C> comparator, final Duration timespan) { 841 requireNonNull(comparator); 842 return sliceBestGatherer((a, b) -> best(comparator, a, b), timespan, systemUTC()); 843 } 844 845 /** 846 * Return a new gatherer which emits the <em>best</em> element consumed 847 * within the given {@code timespan}. 848 * 849 * @param <C> the element type 850 * @param comparator the comparator used for testing the elements 851 * @param timespan the timespan the elements are collected for the 852 * calculation slice 853 * @param clock the {@code clock} used for measuring the {@code timespan} 854 * @return a new gatherer 855 * @throws IllegalArgumentException if the given size is smaller than one 856 * @throws NullPointerException if one of the arguments is {@code null} 857 */ 858 public static <C> Gatherer<C, ?, C> bestOfInterval( 859 final Comparator<? super C> comparator, 860 final Duration timespan, 861 final Clock clock 862 ) { 863 requireNonNull(comparator); 864 return sliceBestGatherer((a, b) -> best(comparator, a, b), timespan, clock); 865 } 866 867 868 private static <C> Gatherer<C, ?, C> sliceBestGatherer( 869 final BinaryOperator<C> comp, 870 final Duration timespan, 871 final Clock clock 872 ) { 873 requireNonNull(comp); 874 requireNonNull(timespan); 875 requireNonNull(clock); 876 877 record State<C>(long ts, AtomicLong start, AtomicReference<C> best) { 878 State(long ts) { 879 this(ts, new AtomicLong(0), new AtomicReference<>()); 880 } 881 } 882 883 return Gatherer.ofSequential( 884 () -> new State<C>(timespan.toMillis()), 885 (state, value, downstream) -> { 886 if (state.start.get() == 0) { 887 state.start.set(clock.millis()); 888 } 889 890 state.best.set(comp.apply(state.best.get(), value)); 891 long end = clock.millis(); 892 893 if (end - state.start.get() >= state.ts) { 894 downstream.push(state.best.get()); 895 state.start.set(0); 896 state.best.set(null); 897 } 898 899 return true; 900 } 901 ); 902 } 903 904}