/* * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package java.util.stream; import jdk.internal.access.SharedSecrets; import jdk.internal.vm.annotation.ForceInline; import java.util.ArrayDeque; import java.util.List; import java.util.Objects; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.FutureTask; import java.util.concurrent.Semaphore; import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.BinaryOperator; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Gatherer.Integrator; import java.util.stream.Gatherer.Downstream; /** * Implementations of {@link Gatherer} that provide useful intermediate * operations, such as windowing functions, folding functions, * transforming elements concurrently, etc. * * @since 24 */ public final class Gatherers { private Gatherers() { } // This class is not intended to be instantiated // Public built-in Gatherers and factory methods for them /** * Returns a Gatherer that gathers elements into windows * -- encounter-ordered groups of elements -- of a fixed size. * If the stream is empty then no window will be produced. * The last window may contain fewer elements than the supplied window size. * *
Example:
* {@snippet lang = java:
* // will contain: [[1, 2, 3], [4, 5, 6], [7, 8]]
* List Example:
* {@snippet lang = java:
* // will contain: [[1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 8]]
* List Example:
* {@snippet lang = java:
* // will contain: Optional["123456789"]
* Optional Example:
* {@snippet lang = java:
* // will contain: ["1", "12", "123", "1234", "12345", "123456", "1234567", "12345678", "123456789"]
* List> windows =
* Stream.of(1,2,3,4,5,6,7,8).gather(Gatherers.windowFixed(3)).toList();
* }
*
* @implSpec Each window produced is an unmodifiable List; calls to any
* mutator method will always cause {@code UnsupportedOperationException}
* to be thrown. There are no guarantees on the implementation type or
* serializability of the produced Lists.
*
* @apiNote For efficiency reasons, windows may be allocated contiguously
* and eagerly. This means that choosing large window sizes for
* small streams may use excessive memory for the duration of
* evaluation of this operation.
*
* @param windowSize the size of the windows
* @param
the type of elements the returned gatherer consumes
* and the contents of the windows it produces
* @return a new gatherer which groups elements into fixed-size windows
* @throws IllegalArgumentException when {@code windowSize} is less than 1
*/
public static Gatherer > windowFixed(int windowSize) {
if (windowSize < 1)
throw new IllegalArgumentException("'windowSize' must be greater than zero");
class FixedWindow {
Object[] window;
int at;
FixedWindow() {
at = 0;
window = new Object[windowSize];
}
boolean integrate(TR element, Downstream super List > downstream) {
window[at++] = element;
if (at < windowSize) {
return true;
} else {
final var oldWindow = window;
window = new Object[windowSize];
at = 0;
return downstream.push(
SharedSecrets.getJavaUtilCollectionAccess()
.listFromTrustedArrayNullsAllowed(oldWindow)
);
}
}
void finish(Downstream super List > downstream) {
if (at > 0 && !downstream.isRejecting()) {
var lastWindow = new Object[at];
System.arraycopy(window, 0, lastWindow, 0, at);
window = null;
at = 0;
downstream.push(
SharedSecrets.getJavaUtilCollectionAccess()
.listFromTrustedArrayNullsAllowed(lastWindow)
);
}
}
}
return Gatherer. >ofSequential(
// Initializer
FixedWindow::new,
// Integrator
Integrator. > windows2 =
* Stream.of(1,2,3,4,5,6,7,8).gather(Gatherers.windowSliding(2)).toList();
*
* // will contain: [[1, 2, 3, 4, 5, 6], [2, 3, 4, 5, 6, 7], [3, 4, 5, 6, 7, 8]]
* List
> windows6 =
* Stream.of(1,2,3,4,5,6,7,8).gather(Gatherers.windowSliding(6)).toList();
* }
*
* @implSpec Each window produced is an unmodifiable List; calls to any
* mutator method will always cause {@code UnsupportedOperationException}
* to be thrown. There are no guarantees on the implementation type or
* serializability of the produced Lists.
*
* @apiNote For efficiency reasons, windows may be allocated contiguously
* and eagerly. This means that choosing large window sizes for
* small streams may use excessive memory for the duration of
* evaluation of this operation.
*
* @param windowSize the size of the windows
* @param
the type of elements the returned gatherer consumes
* and the contents of the windows it produces
* @return a new gatherer which groups elements into sliding windows
* @throws IllegalArgumentException when windowSize is less than 1
*/
public static Gatherer > windowSliding(int windowSize) {
if (windowSize < 1)
throw new IllegalArgumentException("'windowSize' must be greater than zero");
class SlidingWindow {
Object[] window;
int at;
boolean firstWindow;
SlidingWindow() {
firstWindow = true;
at = 0;
window = new Object[windowSize];
}
boolean integrate(TR element, Downstream super List > downstream) {
window[at++] = element;
if (at < windowSize) {
return true;
} else {
final var oldWindow = window;
final var newWindow = new Object[windowSize];
System.arraycopy(oldWindow,1, newWindow, 0, windowSize - 1);
window = newWindow;
at -= 1;
firstWindow = false;
return downstream.push(
SharedSecrets.getJavaUtilCollectionAccess()
.listFromTrustedArrayNullsAllowed(oldWindow)
);
}
}
void finish(Downstream super List > downstream) {
if (firstWindow && at > 0 && !downstream.isRejecting()) {
var lastWindow = new Object[at];
System.arraycopy(window, 0, lastWindow, 0, at);
window = null;
at = 0;
downstream.push(
SharedSecrets.getJavaUtilCollectionAccess()
.listFromTrustedArrayNullsAllowed(lastWindow)
);
}
}
}
return Gatherer. >ofSequential(
// Initializer
SlidingWindow::new,
// Integrator
Integrator.