diff --git a/src/java.base/share/classes/java/lang/LazyConstant.java b/src/java.base/share/classes/java/lang/LazyConstant.java
index 85f9d0e82fd8f..13b1c158c66e2 100644
--- a/src/java.base/share/classes/java/lang/LazyConstant.java
+++ b/src/java.base/share/classes/java/lang/LazyConstant.java
@@ -89,6 +89,10 @@
  * <p>
  * If the computing function recursively invokes itself via the lazy constant, an
  * {@linkplain IllegalStateException} is thrown, and the lazy constant is not initialized.
+ * <p>
+ * If evaluation of the computing function throws a {@linkplain Throwable}, the lazy
+ * constant transitions to an error state. Subsequent {@linkplain #get() get()} calls
+ * throw {@linkplain NoSuchElementException} without invoking the computing function again.
  *
  * <h2 id="composition">Composing lazy constants</h2>
  * A lazy constant can depend on other lazy constants, forming a dependency graph
@@ -161,9 +165,8 @@
  * to a lazy constant.
  *
  * <h2 id="miscellaneous">Miscellaneous</h2>
- * Except for {@linkplain Object#equals(Object) equals(obj)} and
- * {@linkplain #orElse(Object) orElse(other)} parameters, all method parameters
- * must be <em>non-null</em>, or a {@link NullPointerException} will be thrown.
+ * Except for {@linkplain Object#equals(Object) equals(obj)}, all method parameters must
+ * be <em>non-null</em>, 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,
@@ -204,19 +207,6 @@ public sealed interface LazyConstant<T>
         extends Supplier<T>
         permits LazyConstantImpl {
 
-    /**
-     * {@return the contents of this lazy constant if initialized, otherwise,
-     *          returns {@code other}}
-     * <p>
-     * 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}
@@ -224,20 +214,14 @@ public sealed interface LazyConstant<T>
      * After this method returns successfully, the constant is guaranteed to be
      * initialized.
      * <p>
-     * 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.
+     * If the computing function throws, this method relays that {@code Throwable} to the
+     * caller and this lazy constant transitions to an error state. Subsequent invocations
+     * of {@code get()} throw {@code NoSuchElementException}.
+     *
+     * @throws NoSuchElementException if the computing function previously threw
      */
     T get();
 
-    /**
-     * {@return {@code true} if the constant is initialized, {@code false} otherwise}
-     * <p>
-     * 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
 
     /**
diff --git a/src/java.base/share/classes/java/util/List.java b/src/java.base/share/classes/java/util/List.java
index 5f9a90e1748ee..779c279d38bba 100644
--- a/src/java.base/share/classes/java/util/List.java
+++ b/src/java.base/share/classes/java/util/List.java
@@ -1209,15 +1209,18 @@ static <E> List<E> copyOf(Collection<? extends E> coll) {
      * Competing threads accessing an element already under computation will block until
      * an element is computed or the computing function completes abnormally.
      * <p>
-     * If invoking the provided computing function throws an exception, it is rethrown
-     * to the initial caller and no value for the element is recorded.
-     * <p>
      * If the provided computing function returns {@code null},
      * a {@linkplain NullPointerException} will be thrown. Hence, just like other
      * unmodifiable lists created via the {@code List::of} factories, a lazy list
      * cannot contain {@code null} elements. Clients that want to use nullable elements
      * can wrap elements into an {@linkplain Optional} holder.
      * <p>
+     * If evaluation of the computing function throws a {@linkplain Throwable} (for an
+     * index), it is rethrown to the initial caller and no value for the element is
+     * recorded and the associated lazy element transitions to an error state. Subsequent
+     * {@linkplain List#get(int) List::get} calls for the same index throw
+     * {@linkplain NoSuchElementException} without invoking the computing function again.
+     * <p>
      * The elements of any {@link List#subList(int, int) subList()} or
      * {@link List#reversed()} views of the returned list are also lazily computed.
      * <p>
@@ -1232,13 +1235,36 @@ static <E> List<E> copyOf(Collection<? extends E> coll) {
      * {@linkplain Object#equals(Object) equals()},
      * {@linkplain Object#hashCode() hashCode()}, and
      * {@linkplain Object#toString() toString()} methods may trigger initialization of
-     * one or more lazy elements.
+     * one or more lazy elements. If initialization fails for at least one element,
+     * the {@linkplain Object Object methods} may throw {@linkplain NoSuchElementException}.
      * <p>
      * The returned lazy list strongly references its computing
      * function used to compute elements at least as long as there are uninitialized
      * elements.
      * <p>
      * The returned List is <em>not</em> {@linkplain Serializable}.
+     * <p>
+     * The returned {@code List<E>} can be thought of as a list backed by a
+     * {@code List<LazyConstant<E>>} field and where the {@linkplain List#get(int)}
+     * operation is equivalent to:
+     * {@snippet lang = java:
+     * class LazyList<E> extends AbstractList<E> {
+     *
+     *     private final List<LazyConstant<E>> backingList;
+     *
+     *     public LazyList(int size, IntFunction<E> computingFunction) {
+     *         this.backingList = IntStream.range(0, size)
+     *                 .mapToObj(i -> LazyConstant.of(() -> computingFunction.apply(i)))
+     *                 .toList();
+     *     }
+     *
+     *     @Override
+     *     public E get(int index) {
+     *         return backingList.get(index).get();
+     *     }
+     * }
+     *}
+     * Except, performance and storage efficiency might be better.
      *
      * @implNote  after all elements have been initialized successfully, the computing
      *            function is no longer strongly referenced and becomes eligible for
diff --git a/src/java.base/share/classes/java/util/Map.java b/src/java.base/share/classes/java/util/Map.java
index fa16fb89050ac..4c9f68b25fa2f 100644
--- a/src/java.base/share/classes/java/util/Map.java
+++ b/src/java.base/share/classes/java/util/Map.java
@@ -1764,16 +1764,18 @@ static <K, V> Map<K, V> copyOf(Map<? extends K, ? extends V> map) {
      * threads accessing a value already under computation will block until an element
      * is computed or the computing function completes abnormally.
      * <p>
-     * If invoking the provided computing function throws an exception, it
-     * is rethrown to the initial caller and no value associated with the provided key
-     * is recorded.
-     * <p>
      * If the provided computing function returns {@code null},
      * a {@linkplain NullPointerException} will be thrown. Hence, just like other
      * unmodifiable maps created via the {@code Map::of} factories, a lazy map
      * cannot contain {@code null} values. Clients that want to use nullable values can
      * wrap values into an {@linkplain Optional} holder.
      * <p>
+     * If evaluation of the computing function throws a {@linkplain Throwable} (for a
+     * key), it is rethrown to the initial caller and no value for the key is
+     * recorded and the associated value transitions to an error state. Subsequent
+     * {@linkplain Map#get(Object)} map::get} calls for the same key throw
+     * {@linkplain NoSuchElementException} without invoking the computing function again.
+     * <p>
      * The values of any {@link Map#values()} or {@link Map#entrySet()} views of
      * the returned map are also lazily computed.
      * <p>
@@ -1785,13 +1787,47 @@ static <K, V> Map<K, V> copyOf(Map<? extends K, ? extends V> map) {
      * {@linkplain Object#equals(Object) equals()},
      * {@linkplain Object#hashCode() hashCode()}, and
      * {@linkplain Object#toString() toString()} methods may trigger initialization of
-     * one or more lazy elements.
+     * one or more lazy values.  If initialization fails for at least one value,
+     * the {@linkplain Object Object methods} may throw {@linkplain NoSuchElementException}.
      * <p>
      * The returned lazy map strongly references its underlying
      * computing function used to compute values at least as long as there are
      * uncomputed values.
      * <p>
      * The returned Map is <em>not</em> {@linkplain Serializable}.
+     * <p>
+     * If the provided Set of {@code keys} is subsequently modified, the returned Map will
+     * not reflect such modifications.
+     * <p>
+     * The Set of {@code keys} must use {@linkplain Set#equals(Object) equals()} as its
+     * equivalence relation, or its comparison method must be consistent with equals,
+     * otherwise the behavior is unspecified.
+     * <p>
+     * The returned {@code Map<K, V>} can be thought of as a map backed by a
+     * {@code Map<K, LazyConstant<V>>} field and where the {@linkplain Map#get(Object)}
+     * operation is equivalent to:
+     * {@snippet lang = java:
+     * class LazyMap<K, V> extends AbstractMap<K, V> {
+     *
+     *     private final Map<K, LazyConstant<V>> backingMap;
+     *
+     *     public LazyMap(Set<K> keys, Function<K, V> computingFunction) {
+     *         this.backingMap = keys.stream()
+     *                 .collect(Collectors.toUnmodifiableMap(
+     *                         Function.identity(),
+     *                         k -> LazyConstant.of(() -> computingFunction.apply(k))));
+     *     }
+     *
+     *     @Override
+     *     public V get(Object key) {
+     *         var lazyConstant = backingMap.get(key);
+     *         return lazyConstant == null
+     *                 ? null
+     *                 : lazyConstant.get();
+     *     }
+     * }
+     *}
+     * Except, performance and storage efficiency might be better.
      *
      * @implNote  after all values have been initialized successfully, the computing
      *            function is no longer strongly referenced and becomes eligible for
diff --git a/src/java.base/share/classes/java/util/Set.java b/src/java.base/share/classes/java/util/Set.java
index 5ce3bf04c7c48..18464f6e0fc94 100644
--- a/src/java.base/share/classes/java/util/Set.java
+++ b/src/java.base/share/classes/java/util/Set.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2026, 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
@@ -25,6 +25,11 @@
 
 package java.util;
 
+import jdk.internal.javac.PreviewFeature;
+
+import java.io.Serializable;
+import java.util.function.Predicate;
+
 /**
  * A collection that contains no duplicate elements.  More formally, sets
  * contain no pair of elements {@code e1} and {@code e2} such that
@@ -77,7 +82,7 @@
  * Set to behave inconsistently or its contents to appear to change.
  * <li>They disallow {@code null} elements. Attempts to create them with
  * {@code null} elements result in {@code NullPointerException}.
- * <li>They are serializable if all elements are serializable.
+ * <li>Unless otherwise specified, they are serializable if all elements are serializable.
  * <li>They reject duplicate elements at creation time. Duplicate elements
  * passed to a static factory method result in {@code IllegalArgumentException}.
  * <li>The iteration order of set elements is unspecified and is subject to change.
@@ -734,4 +739,144 @@ static <E> Set<E> copyOf(Collection<? extends E> coll) {
             return (Set<E>)Set.of(new HashSet<>(coll).toArray());
         }
     }
+
+    /**
+     * {@return a new lazily computed set whose logical membership for each provided
+     *          {@code elementCandidates} is computed via the provided
+     *          {@code computingFunction} on demand}
+     * <p>
+     * In the following, the term <em>membership status</em> means whether an element is a
+     * logical member or a non-member. The returned set is an
+     * {@linkplain Collection##unmodifiable unmodifiable} set whose element candidates are
+     * known at construction. The set's element membership statuses are lazily computed
+     * via the provided {@code computingFunction} when first accessed (e.g., via
+     * {@linkplain Set#contains(Object) Set::contains}). Once the membership status has
+     * been successfully computed for an element candidate, the associated membership
+     * status is initialized (i.e., either as <em>a logical member</em> or as
+     * <em>a logical non-member</em>).
+     * <p>
+     * The provided computing function is guaranteed to be successfully invoked
+     * at most once per element candidate, even in a multi-threaded environment. Competing
+     * threads accessing an element candidate already under membership status computation
+     * will block until the membership status of the element candidate is computed or the
+     * computing function completes abnormally.
+     * <p>
+     * If invoking the provided computing function throws an exception, it
+     * is rethrown to the initial caller and no membership status associated with the
+     * provided element candidate is recorded.
+     * <p>
+     * If the provided computing function recursively calls itself via
+     * the returned lazy set for the same element candidate, an
+     * {@linkplain IllegalStateException} will be thrown.
+     * <p>
+     * If evaluation of the computing function throws a {@linkplain Throwable} (for an
+     * element candidate), it is rethrown to the initial caller and no membership status
+     * for the element candidate is recorded and the associated lazy membership status
+     * transitions to an error state. Subsequent {@linkplain Set#contains(Object) Set::contains}
+     * calls for the same element candidate throw {@linkplain NoSuchElementException}
+     * without invoking the computing function again.
+     *
+     * The returned set's {@linkplain Object Object methods};
+     * {@linkplain Object#equals(Object) equals()},
+     * {@linkplain Object#hashCode() hashCode()}, and
+     * {@linkplain Object#toString() toString()} methods may trigger initialization of
+     * one or more lazy elements. If initialization fails for at least one element,
+     * the {@linkplain Object Object methods} may throw {@linkplain NoSuchElementException}.
+     * <p>
+     * The returned lazy set strongly references its underlying
+     * computing function used to compute membership status at least as long as there are
+     * uncomputed element candidates.
+     * <p>
+     * The returned Set is <em>not</em> {@linkplain Serializable}.
+     * <p>
+     * Here is an example involving an application that manages various configurable
+     * options -- commonly referred to as "switches" -- that control its behavior. The
+     * state of these switches can be determined through the command line, a configuration
+     * file, or even a database connection. By using a lazy set, we ensure that the states
+     * of these switches are evaluated only once. Once computed, the results are eligible
+     * for constant folding by the JVM:
+     * {@snippet lang = java:
+     * class Application {
+     *
+     *     enum Option{VERBOSE, DRY_RUN, STRICT}
+     *
+     *     // Lazily initialized Set of Options
+     *     static final Set<Option> OPTIONS =
+     *             Set.ofLazy(EnumSet.allOf(Option.class), Application::isEnabled);
+     *
+     *     // Return true when the given Option is enabled
+     *     private static boolean isEnabled(Option option) {
+     *         // Parse command line, read configuration file, load database
+     *         ...
+     *     }
+     *
+     *     public static void process() {
+     *         if (OPTIONS.contains(Option.DRY_RUN)) {
+     *             // Skip processing in DRY_RUN mode
+     *             return;
+     *         }
+     *         // Actual processing logic
+     *     }
+     *
+     * }
+     * }
+     * <p>
+     * If the provided Set of {@code elementCandidates} is subsequently modified, the
+     * returned Set will not reflect such modifications.
+     * <p>
+     * The Set of {@code elementCandidates} must use
+     * {@linkplain Set#equals(Object) equals()} as its equivalence relation, or its
+     * comparison method must be consistent with equals, otherwise the behavior is
+     * unspecified.
+     * <p>
+     * The returned {@code Set<E>} can be thought of as a set backed by a
+     * {@code Map<E, LazyConstant<Boolean>>} field and where the {@linkplain Set#contains(Object)}
+     * operation is equivalent to:
+     * {@snippet lang = java:
+     * class LazySet<E> extends AbstractCollection<E> implements Set<E> {
+     *
+     *     private final Map<E, LazyConstant<Boolean>> backingMap;
+     *
+     *     public LazySet(Set<E> elementCandidates, Predicate<E> computingFunction) {
+     *         this.backingMap = elementCandidates.stream()
+     *                 .collect(Collectors.toUnmodifiableMap(
+     *                         Function.identity(),
+     *                         k -> LazyConstant.of(() -> computingFunction.test(k))));
+     *     }
+     *
+     *     @Override
+     *     public boolean contains(Object o) {
+     *         var lazyConstant = backingMap.get(o);
+     *         return lazyConstant == null
+     *                 ? false
+     *                 : lazyConstant.get();
+     *     }
+     * }
+     *}
+     * Except, performance and storage efficiency might be better.
+     *
+     * @implNote  after all element membership statuses have been initialized successfully,
+     *            the computing function is no longer strongly referenced and becomes
+     *            eligible for garbage collection.
+     *
+     * @param elementCandidates the (non-null) element candidates to be evaluated
+     * @param computingFunction to invoke whenever the membership status of an element
+     *                          candidate is first computed
+     * @param <E>               the type of elements maintained by the returned set
+     * @throws NullPointerException if the provided set of {@code elementCandidates} is
+     *                              {@code null}, if the set of {@code elementCandidates}
+     *                              contains a {@code null} element, or if the provided
+     *                              {@code computingFunction} is {@code null}
+     *
+     * @see LazyConstant
+     * @since 27
+     */
+    @PreviewFeature(feature = PreviewFeature.Feature.LAZY_CONSTANTS)
+    static <E> Set<E> ofLazy(Set<? extends E> elementCandidates,
+                             Predicate<? super E> computingFunction) {
+        Objects.requireNonNull(elementCandidates);
+        Objects.requireNonNull(computingFunction);
+        return LazyCollections.ofLazySet(elementCandidates, computingFunction);
+    }
+
 }
