diff --git a/src/java.base/share/classes/java/lang/LazyConstant.java b/src/java.base/share/classes/java/lang/LazyConstant.java
new file mode 100644
index 00000000000..34f3d754a10
--- /dev/null
+++ b/src/java.base/share/classes/java/lang/LazyConstant.java
@@ -0,0 +1,307 @@
+/*
+ * 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.javac.PreviewFeature;
+import jdk.internal.lang.LazyConstantImpl;
+
+import java.io.Serializable;
+import java.util.*;
+import java.util.function.Function;
+import java.util.function.IntFunction;
+import java.util.function.Supplier;
+
+/**
+ * A lazy constant is a holder of contents that can be set at most once.
+ *
+ * A lazy constant is created using the factory method
+ * {@linkplain LazyConstant#of(Supplier) LazyConstant.of({@code })}.
+ * When created, the lazy constant is not initialized, meaning it has no contents.
+ * The lazy constant (of type {@code T}) can then be initialized
+ * (and its contents retrieved) by calling {@linkplain #get() get()}. The first time
+ * {@linkplain #get() get()} is called, the underlying computing function
+ * (provided at construction) will be invoked and the result will be used to initialize
+ * the constant. Once a lazy constant is initialized, its contents can never change
+ * and will be retrieved over and over again upon subsequent {@linkplain #get() get()}
+ * invocations.
+ *
+ * Consider the following example where a lazy constant field "{@code logger}" holds
+ * an object of type {@code Logger}:
+ *
+ * {@snippet lang = java:
+ * public class Component {
+ *
+ * // Creates a new uninitialized lazy constant
+ * private final LazyConstant logger =
+ * // @link substring="of" target="#of" :
+ * LazyConstant.of( () -> Logger.create(Component.class) );
+ *
+ * public void process() {
+ * logger.get().info("Process started");
+ * // ...
+ * }
+ * }
+ *}
+ *
+ * Initially, the lazy constant is not initialized. When {@code logger.get()}
+ * is first invoked, it evaluates the computing function and initializes the constant to
+ * the result; the result is then returned to the client. Hence, {@linkplain #get() get()}
+ * guarantees that the constant is initialized before it returns, barring
+ * any exceptions.
+ *
+ * Furthermore, {@linkplain #get() get()} guarantees that, out of several threads trying to
+ * invoke the computing function simultaneously, {@linkplain ##thread-safety only one is
+ * ever selected} for computation. This property is crucial as evaluation of the computing
+ * function may have side effects, for example, the call above to {@code Logger.create()}
+ * may result in storage resources being prepared.
+ *
+ *
Exception handling
+ * If the computing function returns {@code null}, a {@linkplain NullPointerException}
+ * is thrown. Hence, a lazy constant can never hold a {@code null} value. Clients who
+ * want to use a nullable constant can wrap the value into an {@linkplain Optional} holder.
+ *
+ * If the computing function recursively invokes itself (directly or indirectly via
+ * the lazy constant), an {@linkplain IllegalStateException} is thrown, and the lazy
+ * constant is not initialized.
+ *
+ *
Composing lazy constants
+ * A lazy constant can depend on other lazy constants, 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 lazy constants:
+ *
+ * {@snippet lang = java:
+ * public final class DependencyUtil {
+ *
+ * private DependencyUtil() {}
+ *
+ * public static class Foo {
+ * // ...
+ * }
+ *
+ * public static class Bar {
+ * public Bar(Foo foo) {
+ * // ...
+ * }
+ * }
+ *
+ * private static final LazyConstant FOO = LazyConstant.of( Foo::new );
+ * private static final LazyConstant BAR = LazyConstant.of( () -> new Bar(FOO.get()) );
+ *
+ * public static Foo foo() {
+ * return FOO.get();
+ * }
+ *
+ * public static Bar bar() {
+ * return BAR.get();
+ * }
+ *
+ * }
+ *}
+ * Calling {@code BAR.get()} will create the {@code Bar} singleton if it is not already
+ * created. Upon such a creation, a dependent {@code Foo} will first be created if
+ * the {@code Foo} does not already exist.
+ *
+ *
Thread Safety
+ * A lazy constant is guaranteed to be initialized atomically and at most once. If
+ * competing threads are racing to initialize a lazy constant, only one updating thread
+ * runs the computing function (which runs on the caller's thread and is hereafter denoted
+ * the computing thread), while the other threads are blocked until the constant
+ * is initialized, after which the other threads observe the lazy constant is initialized
+ * and leave the constant unchanged and will never invoke any computation.
+ *
+ * The invocation of the computing function and the resulting initialization of
+ * the constant {@linkplain java.util.concurrent##MemoryVisibility happens-before}
+ * the initialized constant's content is read. Hence, the initialized constant's content,
+ * including any {@code final} fields of any newly created objects, is safely published.
+ *
+ * Thread interruption does not cancel the initialization of a lazy constant. In other
+ * words, if the computing thread is interrupted, {@code LazyConstant::get} doesn't clear
+ * the interrupted thread’s status, nor does it throw an {@linkplain InterruptedException}.
+ *
+ * If the computing function blocks indefinitely, other threads operating on this
+ * lazy constant may block indefinitely; no timeouts or cancellations are provided.
+ *
+ *
Performance
+ * The contents of a lazy constant can never change after the lazy constant has been
+ * initialized. Therefore, a JVM implementation may, for an initialized lazy constant,
+ * elide all future reads of that lazy constant's contents and instead use the contents
+ * that has been previously observed. We call this optimization constant folding.
+ * This is only possible if there is a direct reference from a {@code static final} field
+ * to a lazy constant or if there is a chain from a {@code static final} field -- via one
+ * or more trusted fields (i.e., {@code static final} fields,
+ * {@linkplain Record record} fields, or final instance fields in hidden classes) --
+ * to a lazy constant.
+ *
+ *
Miscellaneous
+ * Except for {@linkplain Object#equals(Object) equals(obj)} and
+ * {@linkplain #orElse(Object) orElse(other)} parameters, all method parameters
+ * must be non-null, or a {@link NullPointerException} will be thrown.
+ *
+ * @apiNote Once a lazy constant is initialized, its contents cannot ever be removed.
+ * This can be a source of an unintended memory leak. More specifically,
+ * a lazy constant {@linkplain java.lang.ref##reachability strongly references}
+ * it contents. Hence, the contents of a lazy constant will be reachable as long
+ * as the lazy constant itself is reachable.
+ *
+ * While it's possible to store an array inside a lazy constant, doing so will
+ * not result in improved access performance of the array elements. Instead, a
+ * {@linkplain List#ofLazy(int, IntFunction) lazy list} of arbitrary depth can
+ * be used, which provides constant components.
+ *
+ * The {@code LazyConstant} type is not {@link Serializable}.
+ *
+ * Use in static initializers may interact with class initialization order;
+ * cyclic initialization may result in initialization errors as described
+ * in section {@jls 12.4} of The Java Language Specification.
+ *
+ * @implNote
+ * A lazy constant is free to synchronize on itself. Hence, care must be
+ * taken when directly or indirectly synchronizing on a lazy constant.
+ * A lazy constant is unmodifiable but its contents may or may not be
+ * immutable (e.g., it may hold an {@linkplain ArrayList}).
+ *
+ * @param type of the constant
+ *
+ * @since 26
+ *
+ * @see Optional
+ * @see Supplier
+ * @see List#ofLazy(int, IntFunction)
+ * @see Map#ofLazy(Set, Function)
+ * @jls 12.4 Initialization of Classes and Interfaces
+ * @jls 17.4.5 Happens-before Order
+ */
+@PreviewFeature(feature = PreviewFeature.Feature.LAZY_CONSTANTS)
+public sealed interface LazyConstant
+ extends Supplier
+ permits LazyConstantImpl {
+
+ /**
+ * {@return the contents of this lazy constant if initialized, otherwise,
+ * returns {@code other}}
+ *
+ * This method never triggers initialization of this lazy constant and will observe
+ * initialization by other threads atomically (i.e., it returns the contents
+ * if and only if the initialization has already completed).
+ *
+ * @param other value to return if the content is not initialized
+ * (can be {@code null})
+ */
+ T orElse(T other);
+
+ /**
+ * {@return the contents of this initialized constant. If not initialized, first
+ * computes and initializes this constant using the computing function}
+ *
+ * After this method returns successfully, the constant is guaranteed to be
+ * initialized.
+ *
+ * If the computing function throws, the throwable is relayed to the caller and
+ * the lazy constant remains uninitialized; a subsequent call to get() may then
+ * attempt the computation again.
+ */
+ T get();
+
+ /**
+ * {@return {@code true} if the constant is initialized, {@code false} otherwise}
+ *
+ * This method never triggers initialization of this lazy constant and will observe
+ * changes in the initialization state made by other threads atomically.
+ */
+ boolean isInitialized();
+
+ // Object methods
+
+ /**
+ * {@return if this lazy constant is the same as the provided {@code obj}}
+ *
+ * In other words, equals compares the identity of this lazy constant and {@code obj}
+ * to determine equality. Hence, two lazy constants with the same contents are
+ * not equal.
+ *
+ * This method never triggers initialization of this lazy constant.
+ */
+ @Override
+ boolean equals(Object obj);
+
+ /**
+ * {@return the {@linkplain System#identityHashCode(Object) identity hash code} for
+ * this lazy constant}
+ *
+ * This method never triggers initialization of this lazy constant.
+ */
+ @Override
+ int hashCode();
+
+ /**
+ * {@return a string suitable for debugging}
+ *
+ * This method never triggers initialization of this lazy constant and will observe
+ * initialization by other threads atomically (i.e., it observes the
+ * contents if and only if the initialization has already completed).
+ *
+ * If this lazy constant is initialized, an implementation-dependent string
+ * containing the {@linkplain Object#toString()} of the
+ * contents will be returned; otherwise, an implementation-dependent string is
+ * returned that indicates this lazy constant is not yet initialized.
+ */
+ @Override
+ String toString();
+
+ // Factory
+
+ /**
+ * {@return a lazy constant whose contents is to be computed later via the provided
+ * {@code computingFunction}}
+ *
+ * The returned lazy constant strongly references the provided
+ * {@code computingFunction} at least until initialization completes successfully.
+ *
+ * If the provided computing function is already an instance of
+ * {@code LazyConstant}, the method is free to return the provided computing function
+ * directly.
+ *
+ * @implNote after initialization completes successfully, the computing function is
+ * no longer strongly referenced and becomes eligible for
+ * garbage collection.
+ *
+ * @param computingFunction in the form of a {@linkplain Supplier} to be used
+ * to initialize the constant
+ * @param type of the constant
+ *
+ */
+ @SuppressWarnings("unchecked")
+ static LazyConstant of(Supplier extends T> computingFunction) {
+ Objects.requireNonNull(computingFunction);
+ if (computingFunction instanceof LazyConstant extends T> lc) {
+ return (LazyConstant) lc;
+ }
+ return LazyConstantImpl.ofLazy(computingFunction);
+ }
+
+}
diff --git a/src/java.base/share/classes/java/lang/StableValue.java b/src/java.base/share/classes/java/lang/StableValue.java
deleted file mode 100644
index 1815cb1a5b1..00000000000
--- a/src/java.base/share/classes/java/lang/StableValue.java
+++ /dev/null
@@ -1,756 +0,0 @@
-/*
- * 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 #orElseSet(Supplier)} 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 {
-
- /**
- * 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 extends T> supplier);
-
- /**
- * 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
- * @throws IllegalStateException if a supplier invoked by {@link #orElseSet(Supplier)}
- * recursively attempts to set this stable value by calling this method
- * directly or indirectly.
- */
- 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 competing threads will then observe the newly
- * computed value (if any) and will then never execute the {@code underlying} supplier.
- *
- * 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 extends T> 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 upper bound of the range {@code [0, size)} indicating
- * the allowed inputs
- * @param underlying {@code 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 extends R> 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 extends T> inputs,
- Function super T, ? extends R> 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 {@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 extends E> 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 {@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 super K, ? extends V> 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);
- }
-
-}
diff --git a/src/java.base/share/classes/java/nio/charset/Charset.java b/src/java.base/share/classes/java/nio/charset/Charset.java
index 1eb3c9c2094..736ed4f12d5 100644
--- a/src/java.base/share/classes/java/nio/charset/Charset.java
+++ b/src/java.base/share/classes/java/nio/charset/Charset.java
@@ -25,7 +25,6 @@
package java.nio.charset;
-import jdk.internal.misc.ThreadTracker;
import jdk.internal.misc.VM;
import jdk.internal.util.StaticProperty;
import jdk.internal.vm.annotation.Stable;
@@ -41,7 +40,6 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.NoSuchElementException;
-import java.util.Objects;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.SortedMap;
@@ -426,7 +424,7 @@ public abstract class Charset
}
/* The extended set of charsets */
- private static final Supplier> EXTENDED_PROVIDERS = StableValue.supplier(
+ private static final LazyConstant> EXTENDED_PROVIDERS = LazyConstant.of(
new Supplier<>() { public List get() { return extendedProviders0(); }});
private static List extendedProviders0() {
@@ -617,7 +615,7 @@ public abstract class Charset
return Collections.unmodifiableSortedMap(m);
}
- private static final Supplier defaultCharset = StableValue.supplier(
+ private static final LazyConstant defaultCharset = LazyConstant.of(
new Supplier<>() { public Charset get() { return defaultCharset0(); }});
private static Charset defaultCharset0() {
@@ -658,7 +656,7 @@ public abstract class Charset
@Stable
private final String[] aliases;
@Stable
- private final Supplier> aliasSet = StableValue.supplier(
+ private final LazyConstant> aliasSet = LazyConstant.of(
new Supplier<>() { public Set get() { return Set.of(aliases); }});
/**
diff --git a/src/java.base/share/classes/java/util/Currency.java b/src/java.base/share/classes/java/util/Currency.java
index febae04a77b..b254bae32a1 100644
--- a/src/java.base/share/classes/java/util/Currency.java
+++ b/src/java.base/share/classes/java/util/Currency.java
@@ -142,8 +142,8 @@ public final class Currency implements Serializable {
// class data: instance map
private static ConcurrentMap instances = new ConcurrentHashMap<>(7);
- private static final Supplier> available =
- StableValue.supplier(Currency::computeAllCurrencies);
+ private static final LazyConstant> available =
+ LazyConstant.of(Currency::computeAllCurrencies);
// Class data: currency data obtained from currency.data file.
// Purpose:
diff --git a/src/java.base/share/classes/java/util/ImmutableCollections.java b/src/java.base/share/classes/java/util/ImmutableCollections.java
index 1dd7808da20..abc48ff5ed9 100644
--- a/src/java.base/share/classes/java/util/ImmutableCollections.java
+++ b/src/java.base/share/classes/java/util/ImmutableCollections.java
@@ -36,18 +36,12 @@ import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
-import java.util.function.IntFunction;
import java.util.function.Predicate;
-import java.util.function.Supplier;
import java.util.function.UnaryOperator;
import jdk.internal.access.JavaUtilCollectionAccess;
import jdk.internal.access.SharedSecrets;
-import jdk.internal.lang.stable.StableUtil;
-import jdk.internal.lang.stable.StableValueImpl;
import jdk.internal.misc.CDS;
-import jdk.internal.util.ArraysSupport;
-import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.vm.annotation.Stable;
/**
@@ -135,14 +129,6 @@ class ImmutableCollections {
public List listFromTrustedArrayNullsAllowed(Object[] array) {
return ImmutableCollections.listFromTrustedArrayNullsAllowed(array);
}
- public List stableList(int size, IntFunction extends E> mapper) {
- // A stable list is not Serializable, so we cannot return `List.of()` if `size == 0`
- return new StableList<>(size, mapper);
- }
- public Map stableMap(Set keys, Function super K, ? extends V> mapper) {
- // A stable map is not Serializable, so we cannot return `Map.of()` if `keys.isEmpty()`
- return new StableMap<>(keys, mapper);
- }
});
}
}
@@ -450,7 +436,7 @@ class ImmutableCollections {
}
}
- static sealed class SubList extends AbstractImmutableList
+ static final class SubList extends AbstractImmutableList
implements RandomAccess {
@Stable
@@ -462,8 +448,10 @@ class ImmutableCollections {
@Stable
final int size;
- private SubList(AbstractImmutableList root, int offset, int size) {
- assert root instanceof List12 || root instanceof ListN || root instanceof StableList;
+ SubList(AbstractImmutableList root, int offset, int size) {
+ assert root instanceof List12
+ || root instanceof ListN
+ || root instanceof LazyCollections.LazyList;
this.root = root;
this.offset = offset;
this.size = size;
@@ -795,187 +783,6 @@ class ImmutableCollections {
}
}
- @FunctionalInterface
- interface HasStableDelegates {
- StableValueImpl[] delegates();
- }
-
- @jdk.internal.ValueBased
- static final class StableList
- extends AbstractImmutableList
- implements HasStableDelegates {
-
- @Stable
- private final IntFunction extends E> mapper;
- @Stable
- final StableValueImpl[] delegates;
-
- StableList(int size, IntFunction extends E> mapper) {
- this.mapper = mapper;
- this.delegates = StableUtil.array(size);
- }
-
- @Override public boolean isEmpty() { return delegates.length == 0;}
- @Override public int size() { return delegates.length; }
- @Override public Object[] toArray() { return copyInto(new Object[size()]); }
-
- @ForceInline
- @Override
- public E get(int i) {
- final StableValueImpl delegate;
- try {
- delegate = delegates[i];
- } catch (ArrayIndexOutOfBoundsException aioobe) {
- throw new IndexOutOfBoundsException(i);
- }
- return delegate.orElseSet(new Supplier() {
- @Override public E get() { return mapper.apply(i); }});
- }
-
- @Override
- @SuppressWarnings("unchecked")
- public T[] toArray(T[] a) {
- final int size = delegates.length;
- if (a.length < size) {
- // Make a new array of a's runtime type, but my contents:
- T[] n = (T[])Array.newInstance(a.getClass().getComponentType(), size);
- return copyInto(n);
- }
- copyInto(a);
- if (a.length > size) {
- a[size] = null; // null-terminate
- }
- return a;
- }
-
- @Override
- public int indexOf(Object o) {
- final int size = size();
- for (int i = 0; i < size; i++) {
- if (Objects.equals(o, get(i))) {
- return i;
- }
- }
- return -1;
- }
-
- @Override
- public int lastIndexOf(Object o) {
- for (int i = size() - 1; i >= 0; i--) {
- if (Objects.equals(o, get(i))) {
- return i;
- }
- }
- return -1;
- }
-
- @SuppressWarnings("unchecked")
- private T[] copyInto(Object[] a) {
- final int len = delegates.length;
- for (int i = 0; i < len; i++) {
- a[i] = get(i);
- }
- return (T[]) a;
- }
-
- @Override
- public List reversed() {
- return new StableReverseOrderListView<>(this);
- }
-
- @Override
- public List subList(int fromIndex, int toIndex) {
- subListRangeCheck(fromIndex, toIndex, size());
- return StableSubList.fromStableList(this, fromIndex, toIndex);
- }
-
- @Override
- public String toString() {
- return StableUtil.renderElements(this, "StableCollection", delegates);
- }
-
- @Override
- public StableValueImpl[] delegates() {
- return delegates;
- }
-
- private static final class StableSubList extends SubList
- implements HasStableDelegates {
-
- private StableSubList(AbstractImmutableList root, int offset, int size) {
- super(root, offset, size);
- }
-
- @Override
- public List reversed() {
- return new StableReverseOrderListView<>(this);
- }
-
- @Override
- public List subList(int fromIndex, int toIndex) {
- subListRangeCheck(fromIndex, toIndex, size());
- return StableSubList.fromStableSubList(this, fromIndex, toIndex);
- }
-
- @Override
- public String toString() {
- return StableUtil.renderElements(this, "StableCollection", delegates());
- }
-
- @Override
- boolean allowNulls() {
- return true;
- }
-
- @Override
- public StableValueImpl[] delegates() {
- @SuppressWarnings("unchecked")
- final var rootDelegates = ((HasStableDelegates) root).delegates();
- return Arrays.copyOfRange(rootDelegates, offset, offset + size);
- }
-
- static SubList fromStableList(StableList list, int fromIndex, int toIndex) {
- return new StableSubList<>(list, fromIndex, toIndex - fromIndex);
- }
-
- static SubList fromStableSubList(StableSubList parent, int fromIndex, int toIndex) {
- return new StableSubList<>(parent.root, parent.offset + fromIndex, toIndex - fromIndex);
- }
-
- }
-
- private static final class StableReverseOrderListView
- extends ReverseOrderListView.Rand
- implements HasStableDelegates {
-
- private StableReverseOrderListView(List base) {
- super(base, false);
- }
-
- // This method does not evaluate the elements
- @Override
- public String toString() {
- return StableUtil.renderElements(this, "StableCollection", delegates());
- }
-
- @Override
- public List subList(int fromIndex, int toIndex) {
- final int size = base.size();
- subListRangeCheck(fromIndex, toIndex, size);
- return new StableReverseOrderListView<>(base.subList(size - toIndex, size - fromIndex));
- }
-
- @Override
- public StableValueImpl[] delegates() {
- @SuppressWarnings("unchecked")
- final var baseDelegates = ((HasStableDelegates) base).delegates();
- return ArraysSupport.reverse(
- Arrays.copyOf(baseDelegates, baseDelegates.length));
- }
- }
-
- }
-
// ---------- Set Implementations ----------
@jdk.internal.ValueBased
@@ -1614,187 +1421,6 @@ class ImmutableCollections {
}
}
- static final class StableMap
- extends AbstractImmutableMap {
-
- @Stable
- private final Function super K, ? extends V> mapper;
- @Stable
- private final Map> delegate;
-
- StableMap(Set keys, Function super K, ? extends V> mapper) {
- this.mapper = mapper;
- this.delegate = StableUtil.map(keys);
- }
-
- @Override public boolean containsKey(Object o) { return delegate.containsKey(o); }
- @Override public int size() { return delegate.size(); }
- @Override public Set> entrySet() { return StableMapEntrySet.of(this); }
-
- @ForceInline
- @Override
- public V get(Object key) {
- return getOrDefault(key, null);
- }
-
- @ForceInline
- @Override
- public V getOrDefault(Object key, V defaultValue) {
- final StableValueImpl stable = delegate.get(key);
- if (stable == null) {
- return defaultValue;
- }
- @SuppressWarnings("unchecked")
- final K k = (K) key;
- return stable.orElseSet(new Supplier() {
- @Override public V get() { return mapper.apply(k); }});
- }
-
- @jdk.internal.ValueBased
- static final class StableMapEntrySet extends AbstractImmutableSet> {
-
- // Use a separate field for the outer class in order to facilitate
- // a @Stable annotation.
- @Stable
- private final StableMap outer;
-
- @Stable
- private final Set>> delegateEntrySet;
-
- private StableMapEntrySet(StableMap outer) {
- this.outer = outer;
- this.delegateEntrySet = outer.delegate.entrySet();
- }
-
- @Override public Iterator> iterator() { return LazyMapIterator.of(this); }
- @Override public int size() { return delegateEntrySet.size(); }
- @Override public int hashCode() { return outer.hashCode(); }
-
- @Override
- public String toString() {
- return StableUtil.renderMappings(this, "StableCollection", delegateEntrySet, false);
- }
-
- // For @ValueBased
- private static StableMapEntrySet of(StableMap outer) {
- return new StableMapEntrySet<>(outer);
- }
-
- @jdk.internal.ValueBased
- static final class LazyMapIterator implements Iterator> {
-
- // Use a separate field for the outer class in order to facilitate
- // a @Stable annotation.
- @Stable
- private final StableMapEntrySet outer;
-
- @Stable
- private final Iterator>> delegateIterator;
-
- private LazyMapIterator(StableMapEntrySet outer) {
- this.outer = outer;
- this.delegateIterator = outer.delegateEntrySet.iterator();
- }
-
- @Override public boolean hasNext() { return delegateIterator.hasNext(); }
-
- @Override
- public Entry next() {
- final Map.Entry> inner = delegateIterator.next();
- final K k = inner.getKey();
- return new StableEntry<>(k, inner.getValue(), new Supplier() {
- @Override public V get() { return outer.outer.mapper.apply(k); }});
- }
-
- @Override
- public void forEachRemaining(Consumer super Map.Entry> action) {
- final Consumer super Map.Entry>> innerAction =
- new Consumer<>() {
- @Override
- public void accept(Entry> inner) {
- final K k = inner.getKey();
- action.accept(new StableEntry<>(k, inner.getValue(), new Supplier() {
- @Override public V get() { return outer.outer.mapper.apply(k); }}));
- }
- };
- delegateIterator.forEachRemaining(innerAction);
- }
-
- // For @ValueBased
- private static LazyMapIterator of(StableMapEntrySet outer) {
- return new LazyMapIterator<>(outer);
- }
-
- }
- }
-
- private record StableEntry(K getKey, // trick
- StableValueImpl stableValue,
- Supplier extends V> supplier) implements Map.Entry {
-
- @Override public V setValue(V value) { throw uoe(); }
- @Override public V getValue() { return stableValue.orElseSet(supplier); }
- @Override public int hashCode() { return hash(getKey()) ^ hash(getValue()); }
- @Override public String toString() { return getKey() + "=" + stableValue.toString(); }
- @Override public boolean equals(Object o) {
- return o instanceof Map.Entry, ?> e
- && Objects.equals(getKey(), e.getKey())
- // Invoke `getValue()` as late as possible to avoid evaluation
- && Objects.equals(getValue(), e.getValue());
- }
-
- private int hash(Object obj) { return (obj == null) ? 0 : obj.hashCode(); }
- }
-
- @Override
- public Collection values() {
- return StableMapValues.of(this);
- }
-
- @jdk.internal.ValueBased
- static final class StableMapValues extends AbstractImmutableCollection {
-
- // Use a separate field for the outer class in order to facilitate
- // a @Stable annotation.
- @Stable
- private final StableMap, V> outer;
-
- private StableMapValues(StableMap, V> outer) {
- this.outer = outer;
- }
-
- @Override public Iterator iterator() { return outer.new ValueIterator(); }
- @Override public int size() { return outer.size(); }
- @Override public boolean isEmpty() { return outer.isEmpty();}
- @Override public boolean contains(Object v) { return outer.containsValue(v); }
-
- private static final IntFunction[]> GENERATOR = new IntFunction[]>() {
- @Override
- public StableValueImpl>[] apply(int len) {
- return new StableValueImpl>[len];
- }
- };
-
- @Override
- public String toString() {
- final StableValueImpl>[] values = outer.delegate.values().toArray(GENERATOR);
- return StableUtil.renderElements(this, "StableCollection", values);
- }
-
- // For @ValueBased
- private static StableMapValues of(StableMap, V> outer) {
- return new StableMapValues<>(outer);
- }
-
- }
-
- @Override
- public String toString() {
- return StableUtil.renderMappings(this, "StableMap", delegate.entrySet(), true);
- }
-
- }
-
}
// ---------- Serialization Proxy ----------
diff --git a/src/java.base/share/classes/java/util/LazyCollections.java b/src/java.base/share/classes/java/util/LazyCollections.java
new file mode 100644
index 00000000000..0bbdad87ac4
--- /dev/null
+++ b/src/java.base/share/classes/java/util/LazyCollections.java
@@ -0,0 +1,584 @@
+/*
+ * 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.util;
+
+import jdk.internal.misc.Unsafe;
+import jdk.internal.util.ImmutableBitSetPredicate;
+import jdk.internal.vm.annotation.AOTSafeClassInitializer;
+import jdk.internal.vm.annotation.ForceInline;
+import jdk.internal.vm.annotation.Stable;
+
+import java.lang.LazyConstant;
+import java.lang.reflect.Array;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.IntFunction;
+import java.util.function.IntPredicate;
+import java.util.function.Supplier;
+
+/**
+ * Container class for lazy collections implementations. Not part of the public API.
+ */
+@AOTSafeClassInitializer
+final class LazyCollections {
+
+ /**
+ * No instances.
+ */
+ private LazyCollections() { }
+
+ // Unsafe allows LazyCollection classes to be used early in the boot sequence
+ private static final Unsafe UNSAFE = Unsafe.getUnsafe();
+
+ @jdk.internal.ValueBased
+ static final class LazyList
+ extends ImmutableCollections.AbstractImmutableList {
+
+ @Stable
+ private final E[] elements;
+ // Keeping track of `size` separately reduces bytecode size compared to
+ // using `elements.length`.
+ @Stable
+ private final int size;
+ @Stable
+ final FunctionHolder> functionHolder;
+ @Stable
+ private final Mutexes mutexes;
+
+ private LazyList(int size, IntFunction extends E> computingFunction) {
+ this.elements = newGenericArray(size);
+ this.size = size;
+ this.functionHolder = new FunctionHolder<>(computingFunction, size);
+ this.mutexes = new Mutexes(size);
+ super();
+ }
+
+ @Override public boolean isEmpty() { return size == 0; }
+ @Override public int size() { return size; }
+ @Override public Object[] toArray() { return copyInto(new Object[size]); }
+
+ @ForceInline
+ @Override
+ public E get(int i) {
+ final E e = contentsAcquire(offsetFor(Objects.checkIndex(i, size)));
+ return (e != null) ? e : getSlowPath(i);
+ }
+
+ private E getSlowPath(int i) {
+ return orElseComputeSlowPath(elements, i, mutexes, i, functionHolder);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public T[] toArray(T[] a) {
+ if (a.length < size) {
+ // Make a new array of a's runtime type, but my contents:
+ T[] n = (T[]) Array.newInstance(a.getClass().getComponentType(), size);
+ return copyInto(n);
+ }
+ copyInto(a);
+ if (a.length > size) {
+ a[size] = null; // null-terminate
+ }
+ return a;
+ }
+
+ @Override
+ public int indexOf(Object o) {
+ for (int i = 0; i < size; i++) {
+ if (Objects.equals(o, get(i))) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ @Override
+ public int lastIndexOf(Object o) {
+ for (int i = size - 1; i >= 0; i--) {
+ if (Objects.equals(o, get(i))) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ @SuppressWarnings("unchecked")
+ private T[] copyInto(Object[] a) {
+ for (int i = 0; i < size; i++) {
+ a[i] = get(i);
+ }
+ return (T[]) a;
+ }
+
+ @SuppressWarnings("unchecked")
+ @ForceInline
+ private E contentsAcquire(long offset) {
+ return (E) UNSAFE.getReferenceAcquire(elements, offset);
+ }
+
+ }
+
+ static final class LazyEnumMap, V>
+ extends AbstractLazyMap {
+
+ @Stable
+ private final Class enumType;
+ @Stable
+ // We are using a wrapper class here to be able to use a min value of zero that
+ // is also stable.
+ private final Integer min;
+ @Stable
+ private final IntPredicate member;
+
+ public LazyEnumMap(Set set,
+ Class enumType,
+ int min,
+ int backingSize,
+ IntPredicate member,
+ Function super K, ? extends V> computingFunction) {
+ this.enumType = enumType;
+ this.min = min;
+ this.member = member;
+ super(set, set.size(), backingSize, computingFunction);
+ }
+
+ @Override
+ @ForceInline
+ public boolean containsKey(Object o) {
+ return enumType.isAssignableFrom(o.getClass())
+ && member.test(((Enum>) o).ordinal());
+ }
+
+ @ForceInline
+ @Override
+ public V getOrDefault(Object key, V defaultValue) {
+ if (enumType.isAssignableFrom(key.getClass())) {
+ final int ordinal = ((Enum>) key).ordinal();
+ if (member.test(ordinal)) {
+ @SuppressWarnings("unchecked")
+ final K k = (K) key;
+ return orElseCompute(k, indexForAsInt(k));
+ }
+ }
+ return defaultValue;
+ }
+
+ @Override
+ Integer indexFor(K key) {
+ return indexForAsInt(key);
+ }
+
+ private int indexForAsInt(K key) {
+ return key.ordinal() - min;
+ }
+
+ }
+
+ static final class LazyMap
+ extends AbstractLazyMap {
+
+ // Use an unmodifiable map with known entries that are @Stable. Lookups through this map can be folded because
+ // it is created using Map.ofEntrie. This allows us to avoid creating a separate hashing function.
+ @Stable
+ private final Map indexMapper;
+
+ public LazyMap(Set keys, Function super K, ? extends V> computingFunction) {
+ @SuppressWarnings("unchecked")
+ final Entry[] entries = (Entry[]) new Entry, ?>[keys.size()];
+ int i = 0;
+ for (K k : keys) {
+ entries[i] = Map.entry(k, i++);
+ }
+ this.indexMapper = Map.ofEntries(entries);
+ super(keys, i, i, computingFunction);
+ }
+
+ @ForceInline
+ @Override
+ public V getOrDefault(Object key, V defaultValue) {
+ final Integer index = indexMapper.get(key);
+ if (index != null) {
+ @SuppressWarnings("unchecked")
+ final K k = (K) key;
+ return orElseCompute(k, index);
+ }
+ return defaultValue;
+ }
+
+ @Override public boolean containsKey(Object o) { return indexMapper.containsKey(o); }
+
+ @Override
+ Integer indexFor(K key) {
+ return indexMapper.get(key);
+ }
+ }
+
+ static sealed abstract class AbstractLazyMap
+ extends ImmutableCollections.AbstractImmutableMap {
+
+ // This field shadows AbstractMap.keySet which is not @Stable.
+ @Stable
+ Set keySet;
+ // This field shadows AbstractMap.values which is of another type
+ @Stable
+ final V[] values;
+ @Stable
+ Mutexes mutexes;
+ @Stable
+ private final int size;
+ @Stable
+ final FunctionHolder> functionHolder;
+ @Stable
+ private final Set> entrySet;
+
+ private AbstractLazyMap(Set keySet,
+ int size,
+ int backingSize,
+ Function super K, ? extends V> computingFunction) {
+ this.size = size;
+ this.functionHolder = new FunctionHolder<>(computingFunction, size);
+ this.values = newGenericArray(backingSize);
+ this.mutexes = new Mutexes(backingSize);
+ super();
+ this.keySet = keySet;
+ this.entrySet = LazyMapEntrySet.of(this);
+ }
+
+ // Abstract methods
+ @Override public abstract boolean containsKey(Object o);
+ abstract Integer indexFor(K key);
+
+ // Public methods
+ @Override public final int size() { return size; }
+ @Override public final boolean isEmpty() { return size == 0; }
+ @Override public final Set> entrySet() { return entrySet; }
+ @Override public Set keySet() { return keySet; }
+
+ @ForceInline
+ @Override
+ public final V get(Object key) {
+ return getOrDefault(key, null);
+ }
+
+ @SuppressWarnings("unchecked")
+ @ForceInline
+ final V orElseCompute(K key, int index) {
+ final long offset = offsetFor(index);
+ final V v = (V) UNSAFE.getReferenceAcquire(values, offset);
+ if (v != null) {
+ return v;
+ }
+ return orElseComputeSlowPath(values, index, mutexes, key, functionHolder);
+ }
+
+ @jdk.internal.ValueBased
+ static final class LazyMapEntrySet extends ImmutableCollections.AbstractImmutableSet> {
+
+ // Use a separate field for the outer class in order to facilitate
+ // a @Stable annotation.
+ @Stable
+ private final AbstractLazyMap map;
+
+ private LazyMapEntrySet(AbstractLazyMap map) {
+ this.map = map;
+ super();
+ }
+
+ @Override public Iterator> iterator() { return LazyMapIterator.of(map); }
+ @Override public int size() { return map.size(); }
+ @Override public int hashCode() { return map.hashCode(); }
+
+ // For @ValueBased
+ private static LazyMapEntrySet of(AbstractLazyMap outer) {
+ return new LazyMapEntrySet<>(outer);
+ }
+
+ @jdk.internal.ValueBased
+ static final class LazyMapIterator implements Iterator> {
+
+ // Use a separate field for the outer class in order to facilitate
+ // a @Stable annotation.
+ @Stable
+ private final AbstractLazyMap map;
+ @Stable
+ private final Iterator keyIterator;
+
+ private LazyMapIterator(AbstractLazyMap map) {
+ this.map = map;
+ this.keyIterator = map.keySet.iterator();
+ super();
+ }
+
+ @Override public boolean hasNext() { return keyIterator.hasNext(); }
+
+ @Override
+ public Entry next() {
+ final K k = keyIterator.next();
+ return new LazyEntry<>(k, map, map.functionHolder);
+ }
+
+ @Override
+ public void forEachRemaining(Consumer super Entry> action) {
+ final Consumer super K> innerAction =
+ new Consumer<>() {
+ @Override
+ public void accept(K key) {
+ action.accept(new LazyEntry<>(key, map, map.functionHolder));
+ }
+ };
+ keyIterator.forEachRemaining(innerAction);
+ }
+
+ // For @ValueBased
+ private static LazyMapIterator of(AbstractLazyMap map) {
+ return new LazyMapIterator<>(map);
+ }
+
+ }
+ }
+
+ private record LazyEntry(K getKey, // trick
+ AbstractLazyMap map,
+ FunctionHolder> functionHolder) implements Entry {
+
+ @Override public V setValue(V value) { throw ImmutableCollections.uoe(); }
+ @Override public V getValue() { return map.orElseCompute(getKey, map.indexFor(getKey)); }
+ @Override public int hashCode() { return hash(getKey()) ^ hash(getValue()); }
+ @Override public String toString() { return getKey() + "=" + getValue(); }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof Map.Entry, ?> e
+ && Objects.equals(getKey(), e.getKey())
+ // Invoke `getValue()` as late as possible to avoid evaluation
+ && Objects.equals(getValue(), e.getValue());
+ }
+
+ private int hash(Object obj) {
+ return (obj == null) ? 0 : obj.hashCode();
+ }
+ }
+
+ @Override
+ public Collection values() {
+ return LazyMapValues.of(this);
+ }
+
+ @jdk.internal.ValueBased
+ static final class LazyMapValues extends ImmutableCollections.AbstractImmutableCollection {
+
+ // Use a separate field for the outer class in order to facilitate
+ // a @Stable annotation.
+ @Stable
+ private final AbstractLazyMap map;
+
+ private LazyMapValues(AbstractLazyMap map) {
+ this.map = map;
+ super();
+ }
+
+ @Override public Iterator iterator() { return map.new ValueIterator(); }
+ @Override public int size() { return map.size(); }
+ @Override public boolean isEmpty() { return map.isEmpty(); }
+ @Override public boolean contains(Object v) { return map.containsValue(v); }
+
+ // For @ValueBased
+ private static LazyMapValues of(AbstractLazyMap outer) {
+ return new LazyMapValues<>(outer);
+ }
+
+ }
+
+ }
+
+ static final class Mutexes {
+
+ private static final Object TOMB_STONE = new Object();
+
+ // Filled on demand and then discarded once it is not needed anymore.
+ // A mutex element can only transition like so: `null` -> `new Object()` -> `TOMB_STONE`
+ private volatile Object[] mutexes;
+ // Used to detect we have computed all elements and no longer need the `mutexes` array
+ private volatile AtomicInteger counter;
+
+ private Mutexes(int length) {
+ this.mutexes = new Object[length];
+ this.counter = new AtomicInteger(length);
+ }
+
+ @ForceInline
+ private Object acquireMutex(long offset) {
+ assert mutexes != null;
+ // Check if there already is a mutex (Object or TOMB_STONE)
+ final Object mutex = UNSAFE.getReferenceVolatile(mutexes, offset);
+ if (mutex != null) {
+ return mutex;
+ }
+ // Protect against racy stores of mutex candidates
+ final Object candidate = new Object();
+ final Object witness = UNSAFE.compareAndExchangeReference(mutexes, offset, null, candidate);
+ return witness == null ? candidate : witness;
+ }
+
+ private void releaseMutex(long offset) {
+ // Replace the old mutex with a tomb stone since now the old mutex can be collected.
+ UNSAFE.putReference(mutexes, offset, TOMB_STONE);
+ if (counter != null && counter.decrementAndGet() == 0) {
+ mutexes = null;
+ counter = null;
+ }
+ }
+
+ }
+
+ @ForceInline
+ private static long offsetFor(long index) {
+ return Unsafe.ARRAY_OBJECT_BASE_OFFSET + Unsafe.ARRAY_OBJECT_INDEX_SCALE * index;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static E[] newGenericArray(int length) {
+ return (E[]) new Object[length];
+ }
+
+ public static List ofLazyList(int size,
+ IntFunction extends E> computingFunction) {
+ return new LazyList<>(size, computingFunction);
+ }
+
+ public static Map ofLazyMap(Set keys,
+ Function super K, ? extends V> computingFunction) {
+ return new LazyMap<>(keys, computingFunction);
+ }
+
+ @SuppressWarnings("unchecked")
+ public static , V>
+ Map ofLazyMapWithEnumKeys(Set keys,
+ Function super K, ? extends V> computingFunction) {
+ // The input set is not empty
+ final Class enumType = ((E) keys.iterator().next()).getDeclaringClass();
+ final BitSet bitSet = new BitSet(enumType.getEnumConstants().length);
+ int min = Integer.MAX_VALUE;
+ int max = Integer.MIN_VALUE;
+ for (K t : keys) {
+ final int ordinal = ((E) t).ordinal();
+ min = Math.min(min, ordinal);
+ max = Math.max(max, ordinal);
+ bitSet.set(ordinal);
+ }
+ final int backingSize = max - min + 1;
+ final IntPredicate member = ImmutableBitSetPredicate.of(bitSet);
+ return (Map) new LazyEnumMap<>((Set) keys, enumType, min, backingSize, member, (Function) computingFunction);
+ }
+
+ @SuppressWarnings("unchecked")
+ static T orElseComputeSlowPath(final T[] array,
+ final int index,
+ final Mutexes mutexes,
+ final Object input,
+ final FunctionHolder> functionHolder) {
+ final long offset = offsetFor(index);
+ final Object mutex = mutexes.acquireMutex(offset);
+ preventReentry(mutex);
+ synchronized (mutex) {
+ final T t = array[index]; // Plain semantics suffice here
+ if (t == null) {
+ final T newValue = switch (functionHolder.function()) {
+ case IntFunction> iFun -> (T) iFun.apply((int) input);
+ case Function, ?> fun -> ((Function