diff --git a/src/java.base/share/classes/java/util/Locale.java b/src/java.base/share/classes/java/util/Locale.java index 0f8474ec12d..ae6c4af5ec5 100644 --- a/src/java.base/share/classes/java/util/Locale.java +++ b/src/java.base/share/classes/java/util/Locale.java @@ -48,6 +48,7 @@ import java.io.Serializable; import java.text.DateFormat; import java.text.MessageFormat; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; import java.util.spi.LocaleNameProvider; import java.util.stream.Stream; @@ -980,29 +981,36 @@ public final class Locale implements Cloneable, Serializable { return getInstance(baseloc, extensions); } + static Locale getInstance(BaseLocale baseloc, LocaleExtensions extensions) { if (extensions == null) { Locale locale = CONSTANT_LOCALES.get(baseloc); if (locale != null) { return locale; } - return LOCALE_CACHE.computeIfAbsent(baseloc, Locale::createLocale); + return LOCALE_CACHE.computeIfAbsent(baseloc, LOCALE_CREATOR); } else { LocaleKey key = new LocaleKey(baseloc, extensions); - return LOCALE_CACHE.computeIfAbsent(key, Locale::createLocale); + return LOCALE_CACHE.computeIfAbsent(key, LOCALE_CREATOR); } } - private static final ReferencedKeyMap LOCALE_CACHE = ReferencedKeyMap.create(true, ConcurrentHashMap::new); - private static Locale createLocale(Object key) { - if (key instanceof BaseLocale base) { - return new Locale(base, null); + private static final ReferencedKeyMap LOCALE_CACHE + = ReferencedKeyMap.create(true, ReferencedKeyMap.concurrentHashMapSupplier()); + + private static final Function LOCALE_CREATOR = new Function<>() { + @Override + public Locale apply(Object key) { + if (key instanceof BaseLocale base) { + return new Locale(base, null); + } + LocaleKey lk = (LocaleKey)key; + return new Locale(lk.base, lk.exts); } - LocaleKey lk = (LocaleKey)key; - return new Locale(lk.base, lk.exts); - } + }; private static final class LocaleKey { + private final BaseLocale base; private final LocaleExtensions exts; private final int hash; diff --git a/src/java.base/share/classes/jdk/internal/util/ReferencedKeyMap.java b/src/java.base/share/classes/jdk/internal/util/ReferencedKeyMap.java index be392c3ae2d..9c364cd7813 100644 --- a/src/java.base/share/classes/jdk/internal/util/ReferencedKeyMap.java +++ b/src/java.base/share/classes/jdk/internal/util/ReferencedKeyMap.java @@ -99,6 +99,21 @@ public final class ReferencedKeyMap implements Map { */ private final ReferenceQueue stale; + /** + * @return a supplier to create a {@code ConcurrentHashMap} appropriate for use in the + * create methods. + * @param the type of keys maintained by the new map + * @param the type of mapped values + */ + public static Supplier, V>> concurrentHashMapSupplier() { + return new Supplier<>() { + @Override + public Map, V> get() { + return new ConcurrentHashMap<>(); + } + }; + } + /** * Private constructor. * diff --git a/src/java.base/share/classes/jdk/internal/util/ReferencedKeySet.java b/src/java.base/share/classes/jdk/internal/util/ReferencedKeySet.java index 21b940439e0..73ad6f32640 100644 --- a/src/java.base/share/classes/jdk/internal/util/ReferencedKeySet.java +++ b/src/java.base/share/classes/jdk/internal/util/ReferencedKeySet.java @@ -75,6 +75,15 @@ public final class ReferencedKeySet extends AbstractSet { */ final ReferencedKeyMap> map; + /** + * @return a supplier to create a {@code ConcurrentHashMap} appropriate for use in the + * create methods. + * @param the type of elements maintained by this set + */ + public static Supplier, ReferenceKey>> concurrentHashMapSupplier() { + return ReferencedKeyMap.concurrentHashMapSupplier(); + } + /** * Private constructor. * diff --git a/src/java.base/share/classes/sun/util/locale/BaseLocale.java b/src/java.base/share/classes/sun/util/locale/BaseLocale.java index 7e0fc9a2d34..ec2a6a49183 100644 --- a/src/java.base/share/classes/sun/util/locale/BaseLocale.java +++ b/src/java.base/share/classes/sun/util/locale/BaseLocale.java @@ -38,7 +38,7 @@ import jdk.internal.util.StaticProperty; import jdk.internal.vm.annotation.Stable; import java.util.StringJoiner; -import java.util.concurrent.ConcurrentHashMap; +import java.util.function.UnaryOperator; public final class BaseLocale { @@ -93,7 +93,7 @@ public final class BaseLocale { // Interned BaseLocale cache private static final ReferencedKeySet CACHE = - ReferencedKeySet.create(true, ConcurrentHashMap::new); + ReferencedKeySet.create(true, ReferencedKeySet.concurrentHashMapSupplier()); public static final String SEP = "_"; @@ -164,13 +164,21 @@ public final class BaseLocale { // "interned" instance can subsequently be used by the Locale // instance which guarantees the locale components are properly cased/interned. return CACHE.intern(new BaseLocale(language, script, region, variant), - (b) -> new BaseLocale( - LocaleUtils.toLowerString(b.language).intern(), - LocaleUtils.toTitleString(b.script).intern(), - LocaleUtils.toUpperString(b.region).intern(), - b.variant.intern())); + // Avoid lambdas since this may be on the bootstrap path in many locales + INTERNER); } + public static final UnaryOperator INTERNER = new UnaryOperator<>() { + @Override + public BaseLocale apply(BaseLocale b) { + return new BaseLocale( + LocaleUtils.toLowerString(b.language).intern(), + LocaleUtils.toTitleString(b.script).intern(), + LocaleUtils.toUpperString(b.region).intern(), + b.variant.intern()); + } + }; + public static String convertOldISOCodes(String language) { return switch (language) { case "he", "iw" -> OLD_ISO_CODES ? "iw" : "he";