diff --git a/src/java.base/share/classes/java/lang/StableValue.java b/src/java.base/share/classes/java/lang/StableValue.java new file mode 100644 index 0000000000000..b12d6f5e92128 --- /dev/null +++ b/src/java.base/share/classes/java/lang/StableValue.java @@ -0,0 +1,757 @@ +/* + * Copyright (c) 2025, 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.lang; + +import jdk.internal.access.SharedSecrets; +import jdk.internal.javac.PreviewFeature; +import jdk.internal.lang.stable.StableEnumFunction; +import jdk.internal.lang.stable.StableFunction; +import jdk.internal.lang.stable.StableIntFunction; +import jdk.internal.lang.stable.StableSupplier; +import jdk.internal.lang.stable.StableUtil; +import jdk.internal.lang.stable.StableValueImpl; + +import java.io.Serializable; +import java.util.Collection; +import java.util.EnumSet; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.RandomAccess; +import java.util.Set; +import java.util.function.Function; +import java.util.function.IntFunction; +import java.util.function.Supplier; + +/** + * A stable value is a holder of contents that can be set at most once. + *

+ * A {@code StableValue} is typically created using the factory method + * {@linkplain StableValue#of() {@code StableValue.of()}}. When created this way, + * the stable value is unset, which means it holds no contents. + * Its contents, of type {@code T}, can be set by calling + * {@linkplain #trySet(Object) trySet()}, {@linkplain #setOrThrow(Object) setOrThrow()}, + * or {@linkplain #orElseSet(Supplier) orElseSet()}. Once set, the contents + * can never change and can be retrieved by calling {@linkplain #orElseThrow() orElseThrow()} + * , {@linkplain #orElse(Object) orElse()}, or {@linkplain #orElseSet(Supplier) orElseSet()}. + *

+ * Consider the following example where a stable value field "{@code logger}" is a + * shallowly immutable holder of contents of type {@code Logger} and that is initially + * created as unset, which means it holds no contents. Later in the example, the + * state of the "{@code logger}" field is checked and if it is still unset, + * the contents is set: + * + * {@snippet lang = java: + * public class Component { + * + * // Creates a new unset stable value with no contents + * // @link substring="of" target="#of" : + * private final StableValue logger = StableValue.of(); + * + * private Logger getLogger() { + * if (!logger.isSet()) { + * logger.trySet(Logger.create(Component.class)); + * } + * return logger.orElseThrow(); + * } + * + * public void process() { + * getLogger().info("Process started"); + * // ... + * } + * } + *} + *

+ * If {@code getLogger()} is called from several threads, several instances of + * {@code Logger} might be created. However, the contents can only be set at most once + * meaning the first writer wins. + *

+ * In order to guarantee that, even under races, only one instance of {@code Logger} is + * ever created, the {@linkplain #orElseSet(Supplier) orElseSet()} method can be used + * instead, where the contents are lazily computed, and atomically set, via a + * {@linkplain Supplier supplier}. In the example below, the supplier is provided in the + * form of a lambda expression: + * + * {@snippet lang = java: + * public class Component { + * + * // Creates a new unset stable value with no contents + * // @link substring="of" target="#of" : + * private final StableValue logger = StableValue.of(); + * + * private Logger getLogger() { + * return logger.orElseSet( () -> Logger.create(Component.class) ); + * } + * + * public void process() { + * getLogger().info("Process started"); + * // ... + * } + * } + *} + *

+ * The {@code getLogger()} method calls {@code logger.orElseSet()} on the stable value to + * retrieve its contents. If the stable value is unset, then {@code orElseSet()} + * evaluates the given supplier, and sets the contents to the result; the result is then + * returned to the client. In other words, {@code orElseSet()} guarantees that a + * stable value's contents is set before it returns. + *

+ * Furthermore, {@code orElseSet()} guarantees that out of one or more suppliers provided, + * only at most one is ever evaluated, and that one is only ever evaluated once, + * even when {@code logger.orElseSet()} is invoked concurrently. This property is crucial + * as evaluation of the supplier may have side effects, for example, the call above to + * {@code Logger.create()} may result in storage resources being prepared. + * + *

Stable Functions

+ * Stable values provide the foundation for higher-level functional abstractions. A + * stable supplier is a supplier that computes a value and then caches it into + * a backing stable value storage for subsequent use. A stable supplier is created via the + * {@linkplain StableValue#supplier(Supplier) StableValue.supplier()} factory, by + * providing an underlying {@linkplain Supplier} which is invoked when the stable supplier + * is first accessed: + * + * {@snippet lang = java: + * public class Component { + * + * private final Supplier logger = + * // @link substring="supplier" target="#supplier(Supplier)" : + * StableValue.supplier( () -> Logger.getLogger(Component.class) ); + * + * public void process() { + * logger.get().info("Process started"); + * // ... + * } + * } + *} + * A stable supplier encapsulates access to its backing stable value storage. This means + * that code inside {@code Component} can obtain the logger object directly from the + * stable supplier, without having to go through an accessor method like {@code getLogger()}. + *

+ * A stable int function is a function that takes an {@code int} parameter and + * uses it to compute a result that is then cached by the backing stable value storage + * for that parameter value. A stable {@link IntFunction} is created via the + * {@linkplain StableValue#intFunction(int, IntFunction) StableValue.intFunction()} + * factory. Upon creation, the input range (i.e. {@code [0, size)}) is specified together + * with an underlying {@linkplain IntFunction} which is invoked at most once per input + * value. In effect, the stable int function will act like a cache for the underlying + * {@linkplain IntFunction}: + * + * {@snippet lang = java: + * final class PowerOf2Util { + * + * private PowerOf2Util() {} + * + * private static final int SIZE = 6; + * private static final IntFunction UNDERLYING_POWER_OF_TWO = + * v -> 1 << v; + * + * private static final IntFunction POWER_OF_TWO = + * // @link substring="intFunction" target="#intFunction(int,IntFunction)" : + * StableValue.intFunction(SIZE, UNDERLYING_POWER_OF_TWO); + * + * public static int powerOfTwo(int a) { + * return POWER_OF_TWO.apply(a); + * } + * } + * + * int result = PowerOf2Util.powerOfTwo(4); // May eventually constant fold to 16 at runtime + * + *} + * The {@code PowerOf2Util.powerOfTwo()} function is a partial function that only + * allows a subset {@code [0, 5]} of the underlying function's {@code UNDERLYING_POWER_OF_TWO} + * input range. + * + *

+ * A stable function is a function that takes a parameter (of type {@code T}) and + * uses it to compute a result (of type {@code R}) that is then cached by the backing + * stable value storage for that parameter value. A stable function is created via the + * {@linkplain StableValue#function(Set, Function) StableValue.function()} factory. + * Upon creation, the input {@linkplain Set} is specified together with an underlying + * {@linkplain Function} which is invoked at most once per input value. In effect, the + * stable function will act like a cache for the underlying {@linkplain Function}: + * + * {@snippet lang = java: + * class Log2Util { + * + * private Log2Util() {} + * + * private static final Set KEYS = + * Set.of(1, 2, 4, 8, 16, 32); + * private static final UnaryOperator UNDERLYING_LOG2 = + * i -> 31 - Integer.numberOfLeadingZeros(i); + * + * private static final Function LOG2 = + * // @link substring="function" target="#function(Set,Function)" : + * StableValue.function(KEYS, UNDERLYING_LOG2); + * + * public static int log2(int a) { + * return LOG2.apply(a); + * } + * + * } + * + * int result = Log2Util.log2(16); // May eventually constant fold to 4 at runtime + *} + * + * The {@code Log2Util.log2()} function is a partial function that only allows + * a subset {@code {1, 2, 4, 8, 16, 32}} of the underlying function's + * {@code UNDERLYING_LOG2} input range. + * + *

Stable Collections

+ * Stable values can also be used as backing storage for + * {@linkplain Collection##unmodifiable unmodifiable collections}. A stable list + * is an unmodifiable list, backed by an array of stable values. The stable list elements + * are computed when they are first accessed, using a provided {@linkplain IntFunction}: + * + * {@snippet lang = java: + * final class PowerOf2Util { + * + * private PowerOf2Util() {} + * + * private static final int SIZE = 6; + * private static final IntFunction UNDERLYING_POWER_OF_TWO = + * v -> 1 << v; + * + * private static final List POWER_OF_TWO = + * // @link substring="list" target="#list(int,IntFunction)" : + * StableValue.list(SIZE, UNDERLYING_POWER_OF_TWO); + * + * public static int powerOfTwo(int a) { + * return POWER_OF_TWO.get(a); + * } + * } + * + * int result = PowerOf2Util.powerOfTwo(4); // May eventually constant fold to 16 at runtime + * + * } + *

+ * Similarly, a stable map is an unmodifiable map whose keys are known at + * construction. The stable map values are computed when they are first accessed, + * using a provided {@linkplain Function}: + * + * {@snippet lang = java: + * class Log2Util { + * + * private Log2Util() {} + * + * private static final Set KEYS = + * Set.of(1, 2, 4, 8, 16, 32); + * private static final UnaryOperator UNDERLYING_LOG2 = + * i -> 31 - Integer.numberOfLeadingZeros(i); + * + * private static final Map LOG2 = + * // @link substring="map" target="#map(Set,Function)" : + * StableValue.map(CACHED_KEYS, UNDERLYING_LOG2); + * + * public static int log2(int a) { + * return LOG2.get(a); + * } + * + * } + * + * int result = Log2Util.log2(16); // May eventually constant fold to 4 at runtime + * + *} + * + *

Composing stable values

+ * A stable value can depend on other stable values, forming a dependency graph + * that can be lazily computed but where access to individual elements can still be + * performant. In the following example, a single {@code Foo} and a {@code Bar} + * instance (that is dependent on the {@code Foo} instance) are lazily created, both of + * which are held by stable values: + * {@snippet lang = java: + * public final class DependencyUtil { + * + * private DependencyUtil() {} + * + * public static class Foo { + * // ... + * } + * + * public static class Bar { + * public Bar(Foo foo) { + * // ... + * } + * } + * + * private static final Supplier FOO = StableValue.supplier(Foo::new); + * private static final Supplier BAR = StableValue.supplier(() -> new Bar(FOO.get())); + * + * public static Foo foo() { + * return FOO.get(); + * } + * + * public static Bar bar() { + * return BAR.get(); + * } + * + * } + *} + * Calling {@code bar()} will create the {@code Bar} singleton if it is not already + * created. Upon such a creation, the dependent {@code Foo} will first be created if + * the {@code Foo} does not already exist. + *

+ * Another example, which has a more complex dependency graph, is to compute the + * Fibonacci sequence lazily: + * {@snippet lang = java: + * public final class Fibonacci { + * + * private Fibonacci() {} + * + * private static final int MAX_SIZE_INT = 46; + * + * private static final IntFunction FIB = + * StableValue.intFunction(MAX_SIZE_INT, Fibonacci::fib); + * + * public static int fib(int n) { + * return n < 2 + * ? n + * : FIB.apply(n - 1) + FIB.apply(n - 2); + * } + * + * } + *} + * Both {@code FIB} and {@code Fibonacci::fib} recurse into each other. Because the + * stable int function {@code FIB} caches intermediate results, the initial + * computational complexity is reduced from exponential to linear compared to a + * traditional non-caching recursive fibonacci method. Once computed, the VM is free to + * constant-fold expressions like {@code Fibonacci.fib(5)}. + *

+ * The fibonacci example above is a directed acyclic graph (i.e., + * it has no circular dependencies and is therefore a dependency tree): + *{@snippet lang=text : + * + * ___________fib(5)____________ + * / \ + * ____fib(4)____ ____fib(3)____ + * / \ / \ + * fib(3) fib(2) fib(2) fib(1) + * / \ / \ / \ + * fib(2) fib(1) fib(1) fib(0) fib(1) fib(0) + *} + * + * If there are circular dependencies in a dependency graph, a stable value will + * eventually throw an {@linkplain IllegalStateException} upon referencing elements in + * a circularity. + * + *

Thread Safety

+ * The contents of a stable value is guaranteed to be set at most once. If competing + * threads are racing to set a stable value, only one update succeeds, while the other + * updates are blocked until the stable value is set, whereafter the other updates + * observes the stable value is set and leave the stable value unchanged. + *

+ * The at-most-once write operation on a stable value that succeeds + * (e.g. {@linkplain #trySet(Object) trySet()}) + * {@linkplain java.util.concurrent##MemoryVisibility happens-before} + * any successful read operation (e.g. {@linkplain #orElseThrow()}). + * A successful write operation can be either: + *

    + *
  • a {@link #trySet(Object)} that returns {@code true},
  • + *
  • a {@link #setOrThrow(Object)} that does not throw, or
  • + *
  • an {@link #orElseSet(Supplier)} that successfully runs the supplier
  • + *
+ * A successful read operation can be either: + *
    + *
  • a {@link #orElseThrow()} that does not throw,
  • + *
  • a {@link #orElse(Object) orElse(other)} that does not return the {@code other} value
  • + *
  • an {@link #orElseSet(Supplier)} that does not {@code throw}, or
  • + *
  • an {@link #isSet()} that returns {@code true}
  • + *
+ *

+ * The method {@link #orElseSet(Supplier)} guarantees that the provided + * {@linkplain Supplier} is invoked successfully at most once, even under race. + * Invocations of {@link #setOrThrow(Object)} form a total order of zero or more + * exceptional invocations followed by zero (if the contents were already set) or one + * successful invocation. Since stable functions and stable collections are built on top + * of the same principles as {@linkplain StableValue#orElseSet(Supplier) orElseSet()} they + * too are thread safe and guarantee at-most-once-per-input invocation. + * + *

Performance

+ * As the contents of a stable value can never change after it has been set, a JVM + * implementation may, for a set stable value, elide all future reads of that + * stable value, and instead directly use any contents that it has previously observed. + * This is true if the reference to the stable value is a constant (e.g. in cases where + * the stable value itself is stored in a {@code static final} field). Stable functions + * and collections are built on top of StableValue. As such, they might also be eligible + * for the same JVM optimizations as for StableValue. + * + * @implSpec Implementing classes of {@code StableValue} are free to synchronize on + * {@code this} and consequently, it should be avoided to + * (directly or indirectly) synchronize on a {@code StableValue}. Hence, + * synchronizing on {@code this} may lead to deadlock. + *

+ * Except for a {@code StableValue}'s contents itself, + * an {@linkplain #orElse(Object) orElse(other)} parameter, and + * an {@linkplain #equals(Object) equals(obj)} parameter; all + * method parameters must be non-null or a {@link NullPointerException} + * will be thrown. + * + * @implNote A {@code StableValue} is mainly intended to be a non-public field in + * a class and is usually neither exposed directly via accessors nor passed as + * a method parameter. + *

+ * Stable functions and collections make reasonable efforts to provide + * {@link Object#toString()} operations that do not trigger evaluation + * of the internal stable values when called. + * Stable collections have {@link Object#equals(Object)} operations that try + * to minimize evaluation of the internal stable values when called. + *

+ * As objects can be set via stable values but never removed, this can be a + * source of unintended memory leaks. A stable value's contents are + * {@linkplain java.lang.ref##reachability strongly reachable}. + * Be advised that reachable stable values will hold their set contents until + * the stable value itself is collected. + *

+ * A {@code StableValue} that has a type parameter {@code T} that is an array + * type (of arbitrary rank) will only allow the JVM to treat the + * array reference as a stable value but not its components. + * Instead, a {@linkplain #list(int, IntFunction) a stable list} of arbitrary + * depth can be used, which provides stable components. More generally, a + * stable value can hold other stable values of arbitrary depth and still + * provide transitive constantness. + *

+ * Stable values, functions, and collections are not {@link Serializable}. + * + * @param type of the contents + * + * @since 25 + */ +@PreviewFeature(feature = PreviewFeature.Feature.STABLE_VALUES) +public sealed interface StableValue + permits StableValueImpl { + + // Principal methods + + /** + * Tries to set the contents of this StableValue to the provided {@code contents}. + * The contents of this StableValue can only be set once, implying this method only + * returns {@code true} once. + *

+ * When this method returns, the contents of this StableValue is always set. + * + * @return {@code true} if the contents of this StableValue was set to the + * provided {@code contents}, {@code false} otherwise + * @param contents to set + * @throws IllegalStateException if a supplier invoked by {@link #orElseSet(Supplier)} + * recursively attempts to set this stable value by calling this method + * directly or indirectly. + */ + boolean trySet(T contents); + + /** + * {@return the contents if set, otherwise, returns the provided {@code other} value} + * + * @param other to return if the contents is not set + */ + T orElse(T other); + + /** + * {@return the contents if set, otherwise, throws {@code NoSuchElementException}} + * + * @throws NoSuchElementException if no contents is set + */ + T orElseThrow(); + + /** + * {@return {@code true} if the contents is set, {@code false} otherwise} + */ + boolean isSet(); + + /** + * {@return the contents; if unset, first attempts to compute and set the + * contents using the provided {@code supplier}} + *

+ * The provided {@code supplier} is guaranteed to be invoked at most once if it + * completes without throwing an exception. If this method is invoked several times + * with different suppliers, only one of them will be invoked provided it completes + * without throwing an exception. + *

+ * If the supplier throws an (unchecked) exception, the exception is rethrown and no + * contents is set. The most common usage is to construct a new object serving + * as a lazily computed value or memoized result, as in: + * + * {@snippet lang=java: + * Value v = stable.orElseSet(Value::new); + * } + *

+ * When this method returns successfully, the contents is always set. + *

+ * The provided {@code supplier} will only be invoked once even if invoked from + * several threads unless the {@code supplier} throws an exception. + * + * @param supplier to be used for computing the contents, if not previously set + * @throws IllegalStateException if the provided {@code supplier} recursively + * attempts to set this stable value. + */ + T orElseSet(Supplier supplier); + + // Convenience methods + + /** + * Sets the contents of this StableValue to the provided {@code contents}, or, if + * already set, throws {@code IllegalStateException}. + *

+ * When this method returns (or throws an exception), the contents is always set. + * + * @param contents to set + * @throws IllegalStateException if the contents was already set + */ + void setOrThrow(T contents); + + // Object methods + + /** + * {@return {@code true} if {@code this == obj}, {@code false} otherwise} + * + * @param obj to check for equality + */ + boolean equals(Object obj); + + /** + * {@return the {@linkplain System#identityHashCode(Object) identity hash code} of + * {@code this} object} + */ + int hashCode(); + + // Factories + + /** + * {@return a new unset stable value} + *

+ * An unset stable value has no contents. + * + * @param type of the contents + */ + static StableValue of() { + return StableValueImpl.of(); + } + + /** + * {@return a new pre-set stable value with the provided {@code contents}} + * + * @param contents to set + * @param type of the contents + */ + static StableValue of(T contents) { + final StableValue stableValue = StableValue.of(); + stableValue.trySet(contents); + return stableValue; + } + + /** + * {@return a new stable supplier} + *

+ * The returned {@linkplain Supplier supplier} is a caching supplier that records + * the value of the provided {@code underlying} supplier upon being first accessed via + * the returned supplier's {@linkplain Supplier#get() get()} method. + *

+ * The provided {@code underlying} supplier is guaranteed to be successfully invoked + * at most once even in a multi-threaded environment. Competing threads invoking the + * returned supplier's {@linkplain Supplier#get() get()} method when a value is + * already under computation will block until a value is computed or an exception is + * thrown by the computing thread. The computing threads will then observe the newly + * computed value (if any) and will then never execute. + *

+ * If the provided {@code underlying} supplier throws an exception, it is rethrown + * to the initial caller and no contents is recorded. + *

+ * If the provided {@code underlying} supplier recursively calls the returned + * supplier, an {@linkplain IllegalStateException} will be thrown. + * + * @param underlying supplier used to compute a cached value + * @param the type of results supplied by the returned supplier + */ + static Supplier supplier(Supplier underlying) { + Objects.requireNonNull(underlying); + return StableSupplier.of(underlying); + } + + /** + * {@return a new stable {@linkplain IntFunction}} + *

+ * The returned function is a caching function that, for each allowed {@code int} + * input, records the values of the provided {@code underlying} + * function upon being first accessed via the returned function's + * {@linkplain IntFunction#apply(int) apply()} method. If the returned function is + * invoked with an input that is not in the range {@code [0, size)}, an + * {@link IllegalArgumentException} will be thrown. + *

+ * The provided {@code underlying} function is guaranteed to be successfully invoked + * at most once per allowed input, even in a multi-threaded environment. Competing + * threads invoking the returned function's + * {@linkplain IntFunction#apply(int) apply()} method when a value is already under + * computation will block until a value is computed or an exception is thrown by + * the computing thread. + *

+ * If invoking the provided {@code underlying} function throws an exception, it is + * rethrown to the initial caller and no contents is recorded. + *

+ * If the provided {@code underlying} function recursively calls the returned + * function for the same input, an {@linkplain IllegalStateException} will + * be thrown. + * + * @param size the size of the allowed inputs in the continuous + * interval {@code [0, size)} + * @param underlying IntFunction used to compute cached values + * @param the type of results delivered by the returned IntFunction + * @throws IllegalArgumentException if the provided {@code size} is negative. + */ + static IntFunction intFunction(int size, + IntFunction underlying) { + StableUtil.assertSizeNonNegative(size); + Objects.requireNonNull(underlying); + return StableIntFunction.of(size, underlying); + } + + /** + * {@return a new stable {@linkplain Function}} + *

+ * The returned function is a caching function that, for each allowed + * input in the given set of {@code inputs}, records the values of the provided + * {@code underlying} function upon being first accessed via the returned function's + * {@linkplain Function#apply(Object) apply()} method. If the returned function is + * invoked with an input that is not in {@code inputs}, an {@link IllegalArgumentException} + * will be thrown. + *

+ * The provided {@code underlying} function is guaranteed to be successfully invoked + * at most once per allowed input, even in a multi-threaded environment. Competing + * threads invoking the returned function's {@linkplain Function#apply(Object) apply()} + * method when a value is already under computation will block until a value is + * computed or an exception is thrown by the computing thread. + *

+ * If invoking the provided {@code underlying} function throws an exception, it is + * rethrown to the initial caller and no contents is recorded. + *

+ * If the provided {@code underlying} function recursively calls the returned + * function for the same input, an {@linkplain IllegalStateException} will + * be thrown. + * + * @param inputs the set of (non-null) allowed input values + * @param underlying {@code Function} used to compute cached values + * @param the type of the input to the returned Function + * @param the type of results delivered by the returned Function + * @throws NullPointerException if the provided set of {@code inputs} contains a + * {@code null} element. + */ + static Function function(Set inputs, + Function underlying) { + Objects.requireNonNull(inputs); + // Checking that the Set of inputs does not contain a `null` value is made in the + // implementing classes. + Objects.requireNonNull(underlying); + return inputs instanceof EnumSet && !inputs.isEmpty() + ? StableEnumFunction.of(inputs, underlying) + : StableFunction.of(inputs, underlying); + } + + /** + * {@return a new stable list with the provided {@code size}} + *

+ * The returned list is an {@linkplain Collection##unmodifiable unmodifiable} list + * with the provided {@code size}. The list's elements are computed via the + * provided {@code mapper} when they are first accessed + * (e.g. via {@linkplain List#get(int) List::get}). + *

+ * The provided {@code mapper} function is guaranteed to be successfully invoked + * at most once per list index, even in a multi-threaded environment. Competing + * threads accessing an element already under computation will block until an element + * is computed or an exception is thrown by the computing thread. + *

+ * If invoking the provided {@code mapper} function throws an exception, it + * is rethrown to the initial caller and no value for the element is recorded. + *

+ * Any direct {@link List#subList(int, int) subList} or {@link List#reversed()} views + * of the returned list are also stable. + *

+ * The returned list and its {@link List#subList(int, int) subList} or + * {@link List#reversed()} views implement the {@link RandomAccess} interface. + *

+ * The returned list is unmodifiable and does not implement the + * {@linkplain Collection##optional-operation optional operations} in the + * {@linkplain List} interface. + *

+ * If the provided {@code mapper} recursively calls the returned list for the + * same index, an {@linkplain IllegalStateException} will be thrown. + * + * @param size the size of the returned list + * @param mapper to invoke whenever an element is first accessed + * (may return {@code null}) + * @param the type of elements in the returned list + * @throws IllegalArgumentException if the provided {@code size} is negative. + */ + static List list(int size, + IntFunction mapper) { + StableUtil.assertSizeNonNegative(size); + Objects.requireNonNull(mapper); + return SharedSecrets.getJavaUtilCollectionAccess().stableList(size, mapper); + } + + /** + * {@return a new stable map with the provided {@code keys}} + *

+ * The returned map is an {@linkplain Collection##unmodifiable unmodifiable} map whose + * keys are known at construction. The map's values are computed via the provided + * {@code mapper} when they are first accessed + * (e.g. via {@linkplain Map#get(Object) Map::get}). + *

+ * The provided {@code mapper} function is guaranteed to be successfully invoked + * at most once per key, even in a multi-threaded environment. Competing + * threads accessing a value already under computation will block until an element + * is computed or an exception is thrown by the computing thread. + *

+ * If invoking the provided {@code mapper} function throws an exception, it + * is rethrown to the initial caller and no value associated with the provided key + * is recorded. + *

+ * Any direct {@link Map#values()} or {@link Map#entrySet()} views + * of the returned map are also stable. + *

+ * The returned map is unmodifiable and does not implement the + * {@linkplain Collection##optional-operations optional operations} in the + * {@linkplain Map} interface. + *

+ * If the provided {@code mapper} recursively calls the returned map for + * the same key, an {@linkplain IllegalStateException} will be thrown. + * + * @param keys the (non-null) keys in the returned map + * @param mapper to invoke whenever an associated value is first accessed + * (may return {@code null}) + * @param the type of keys maintained by the returned map + * @param the type of mapped values in the returned map + * @throws NullPointerException if the provided set of {@code inputs} contains a + * {@code null} element. + */ + static Map map(Set keys, + Function mapper) { + Objects.requireNonNull(keys); + // Checking that the Set of keys does not contain a `null` value is made in the + // implementing class. + Objects.requireNonNull(mapper); + return SharedSecrets.getJavaUtilCollectionAccess().stableMap(keys, mapper); + } + +}