8358520: Improve lazy computation in BreakIteratorResourceBundle and related classes

Reviewed-by: naoto, jlu
This commit is contained in:
Per Minborg 2025-06-09 07:00:51 +00:00
parent 91f12600d2
commit 52338c94f6
3 changed files with 65 additions and 87 deletions

View File

@ -54,6 +54,7 @@ import java.net.URL;
import java.net.URLConnection;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Supplier;
import java.util.jar.JarEntry;
import java.util.spi.ResourceBundleControlProvider;
import java.util.spi.ResourceBundleProvider;
@ -487,7 +488,20 @@ public abstract class ResourceBundle {
/**
* A Set of the keys contained only in this ResourceBundle.
*/
private volatile Set<String> keySet;
private final Supplier<Set<String>> keySet = StableValue.supplier(
new Supplier<>() { public Set<String> get() { return keySet0(); }});
private Set<String> keySet0() {
final Set<String> keys = new HashSet<>();
final Enumeration<String> enumKeys = getKeys();
while (enumKeys.hasMoreElements()) {
final String key = enumKeys.nextElement();
if (handleGetObject(key) != null) {
keys.add(key);
}
}
return keys;
}
/**
* Sole constructor. (For invocation by subclass constructors, typically
@ -2298,22 +2312,7 @@ public abstract class ResourceBundle {
* @since 1.6
*/
protected Set<String> handleKeySet() {
if (keySet == null) {
synchronized (this) {
if (keySet == null) {
Set<String> keys = new HashSet<>();
Enumeration<String> enumKeys = getKeys();
while (enumKeys.hasMoreElements()) {
String key = enumKeys.nextElement();
if (handleGetObject(key) != null) {
keys.add(key);
}
}
keySet = keys;
}
}
}
return keySet;
return keySet.get();
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 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
@ -30,6 +30,7 @@ import java.util.Collections;
import java.util.Enumeration;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.function.Supplier;
/**
* BreakIteratorResourceBundle is an abstract class for loading BreakIterator
@ -49,7 +50,15 @@ public abstract class BreakIteratorResourceBundle extends ResourceBundle {
// those keys must be added to NON_DATA_KEYS.
private static final Set<String> NON_DATA_KEYS = Set.of("BreakIteratorClasses");
private volatile Set<String> keys;
private final Supplier<Set<String>> keys = StableValue.supplier(
new Supplier<>() { public Set<String> get() { return keys0(); }});
private Set<String> keys0() {
final ResourceBundle info = getBreakIteratorInfo();
final Set<String> k = info.keySet();
k.removeAll(NON_DATA_KEYS);
return k;
}
/**
* Returns an instance of the corresponding {@code BreakIteratorInfo} (basename).
@ -84,16 +93,6 @@ public abstract class BreakIteratorResourceBundle extends ResourceBundle {
@Override
protected Set<String> handleKeySet() {
if (keys == null) {
ResourceBundle info = getBreakIteratorInfo();
Set<String> k = info.keySet();
k.removeAll(NON_DATA_KEYS);
synchronized (this) {
if (keys == null) {
keys = k;
}
}
}
return keys;
return keys.get();
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 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
@ -44,8 +44,11 @@ import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.function.Supplier;
import sun.util.ResourceBundleEnumeration;
/**
@ -69,12 +72,9 @@ public abstract class OpenListResourceBundle extends ResourceBundle {
// Implements java.util.ResourceBundle.handleGetObject; inherits javadoc specification.
@Override
protected Object handleGetObject(String key) {
if (key == null) {
throw new NullPointerException();
}
loadLookupTablesIfNecessary();
return lookup.get(key); // this class ignores locales
Objects.requireNonNull(key);
return lookup.get()
.get(key); // this class ignores locales
}
/**
@ -93,26 +93,13 @@ public abstract class OpenListResourceBundle extends ResourceBundle {
*/
@Override
protected Set<String> handleKeySet() {
loadLookupTablesIfNecessary();
return lookup.keySet();
return lookup.get()
.keySet();
}
@Override
public Set<String> keySet() {
if (keyset != null) {
return keyset;
}
Set<String> ks = createSet();
ks.addAll(handleKeySet());
if (parent != null) {
ks.addAll(parent.keySet());
}
synchronized (this) {
if (keyset == null) {
keyset = ks;
}
}
return keyset;
return keyset.get();
}
/**
@ -120,38 +107,6 @@ public abstract class OpenListResourceBundle extends ResourceBundle {
*/
protected abstract Object[][] getContents();
/**
* Load lookup tables if they haven't been loaded already.
*/
void loadLookupTablesIfNecessary() {
if (lookup == null) {
loadLookup();
}
}
/**
* We lazily load the lookup hashtable. This function does the
* loading.
*/
private void loadLookup() {
Object[][] contents = getContents();
Map<String, Object> temp = createMap(contents.length);
for (int i = 0; i < contents.length; ++i) {
// key must be non-null String, value must be non-null
String key = (String) contents[i][0];
Object value = contents[i][1];
if (key == null || value == null) {
throw new NullPointerException();
}
temp.put(key, value);
}
synchronized (this) {
if (lookup == null) {
lookup = temp;
}
}
}
/**
* Lets subclasses provide specialized Map implementations.
* Default uses HashMap.
@ -164,6 +119,31 @@ public abstract class OpenListResourceBundle extends ResourceBundle {
return new HashSet<>();
}
private volatile Map<String, Object> lookup;
private volatile Set<String> keyset;
private final Supplier<Map<String, Object>> lookup = StableValue.supplier(
new Supplier<>() { public Map<String, Object> get() { return lookup0(); }});
private Map<String, Object> lookup0() {
final Object[][] contents = getContents();
final Map<String, Object> temp = createMap(contents.length);
for (Object[] content : contents) {
// key must be non-null String, value must be non-null
final String key = Objects.requireNonNull((String) content[0]);
final Object value = Objects.requireNonNull(content[1]);
temp.put(key, value);
}
return temp;
}
private final Supplier<Set<String>> keyset = StableValue.supplier(
new Supplier<>() { public Set<String> get() { return keyset0(); }});
private Set<String> keyset0() {
final Set<String> ks = createSet();
ks.addAll(handleKeySet());
if (parent != null) {
ks.addAll(parent.keySet());
}
return ks;
}
}