< prev index next >
src/java.base/share/classes/java/util/Properties.java
Print this page
@@ -32,10 +32,16 @@
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.io.OutputStreamWriter;
import java.io.BufferedWriter;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.Function;
import jdk.internal.util.xml.PropertiesDefaultHandler;
/**
* The {@code Properties} class represents a persistent set of
@@ -142,10 +148,13 @@
* Creates an empty property list with the specified defaults.
*
* @param defaults the defaults.
*/
public Properties(Properties defaults) {
+ // use package-private constructor to
+ // initialize unused fields with dummy values
+ super((Void) null);
this.defaults = defaults;
}
/**
* Calls the <tt>Hashtable</tt> method {@code put}. Provided for
@@ -158,11 +167,11 @@
* @return the previous value of the specified key in this property
* list, or {@code null} if it did not have one.
* @see #getProperty
* @since 1.2
*/
- public synchronized Object setProperty(String key, String value) {
+ public Object setProperty(String key, String value) {
return put(key, value);
}
/**
@@ -310,11 +319,11 @@
* @throws IllegalArgumentException if a malformed Unicode escape
* appears in the input.
* @throws NullPointerException if {@code reader} is null.
* @since 1.6
*/
- public synchronized void load(Reader reader) throws IOException {
+ public void load(Reader reader) throws IOException {
Objects.requireNonNull(reader, "reader parameter is null");
load0(new LineReader(reader));
}
/**
@@ -336,11 +345,11 @@
* @throws IllegalArgumentException if the input stream contains a
* malformed Unicode escape sequence.
* @throws NullPointerException if {@code inStream} is null.
* @since 1.2
*/
- public synchronized void load(InputStream inStream) throws IOException {
+ public void load(InputStream inStream) throws IOException {
Objects.requireNonNull(inStream, "inStream parameter is null");
load0(new LineReader(inStream));
}
private void load0 (LineReader lr) throws IOException {
@@ -827,23 +836,21 @@
if (comments != null) {
writeComments(bw, comments);
}
bw.write("#" + new Date().toString());
bw.newLine();
- synchronized (this) {
- for (Enumeration<?> e = keys(); e.hasMoreElements();) {
- String key = (String)e.nextElement();
- String val = (String)get(key);
+ for (Map.Entry<Object, Object> e : entrySet()) {
+ String key = (String)e.getKey();
+ String val = (String)e.getValue();
key = saveConvert(key, true, escUnicode);
/* No need to escape embedded and trailing spaces for value, hence
* pass false to flag.
*/
val = saveConvert(val, false, escUnicode);
bw.write(key + "=" + val);
bw.newLine();
}
- }
bw.flush();
}
/**
* Loads all of the properties represented by the XML document on the
@@ -874,11 +881,11 @@
* @see #storeToXML(OutputStream, String, String)
* @see <a href="http://www.w3.org/TR/REC-xml/#charencoding">Character
* Encoding in Entities</a>
* @since 1.5
*/
- public synchronized void loadFromXML(InputStream in)
+ public void loadFromXML(InputStream in)
throws IOException, InvalidPropertiesFormatException
{
Objects.requireNonNull(in);
PropertiesDefaultHandler handler = new PropertiesDefaultHandler();
handler.load(this, in);
@@ -969,11 +976,11 @@
* @return the value in this property list with the specified key value.
* @see #setProperty
* @see #defaults
*/
public String getProperty(String key) {
- Object oval = super.get(key);
+ Object oval = map.get(key);
String sval = (oval instanceof String) ? (String)oval : null;
return ((sval == null) && (defaults != null)) ? defaults.getProperty(key) : sval;
}
/**
@@ -1007,11 +1014,11 @@
* @see java.util.Enumeration
* @see java.util.Properties#defaults
* @see #stringPropertyNames
*/
public Enumeration<?> propertyNames() {
- Hashtable<String,Object> h = new Hashtable<>();
+ Hashtable<String, Object> h = new Hashtable<>();
enumerate(h);
return h.keys();
}
/**
@@ -1031,11 +1038,11 @@
* including the keys in the default property list.
* @see java.util.Properties#defaults
* @since 1.6
*/
public Set<String> stringPropertyNames() {
- Hashtable<String, String> h = new Hashtable<>();
+ Map<String, String> h = new HashMap<>();
enumerateStringProperties(h);
return h.keySet();
}
/**
@@ -1046,15 +1053,15 @@
* @throws ClassCastException if any key in this property list
* is not a string.
*/
public void list(PrintStream out) {
out.println("-- listing properties --");
- Hashtable<String,Object> h = new Hashtable<>();
+ Map<String, Object> h = new HashMap<>();
enumerate(h);
- for (Enumeration<String> e = h.keys() ; e.hasMoreElements() ;) {
- String key = e.nextElement();
- String val = (String)h.get(key);
+ for (Map.Entry<String, Object> e : h.entrySet()) {
+ String key = e.getKey();
+ String val = (String)e.getValue();
if (val.length() > 40) {
val = val.substring(0, 37) + "...";
}
out.println(key + "=" + val);
}
@@ -1074,50 +1081,50 @@
* method is duplicated in order to ensure that a non-1.1 compiler can
* compile this file.
*/
public void list(PrintWriter out) {
out.println("-- listing properties --");
- Hashtable<String,Object> h = new Hashtable<>();
+ Map<String, Object> h = new HashMap<>();
enumerate(h);
- for (Enumeration<String> e = h.keys() ; e.hasMoreElements() ;) {
- String key = e.nextElement();
- String val = (String)h.get(key);
+ for (Map.Entry<String, Object> e : h.entrySet()) {
+ String key = e.getKey();
+ String val = (String)e.getValue();
if (val.length() > 40) {
val = val.substring(0, 37) + "...";
}
out.println(key + "=" + val);
}
}
/**
- * Enumerates all key/value pairs in the specified hashtable.
- * @param h the hashtable
+ * Enumerates all key/value pairs in the specified Map.
+ * @param h the Map
* @throws ClassCastException if any of the property keys
* is not of String type.
*/
- private synchronized void enumerate(Hashtable<String,Object> h) {
+ private void enumerate(Map<String, Object> h) {
if (defaults != null) {
defaults.enumerate(h);
}
- for (Enumeration<?> e = keys() ; e.hasMoreElements() ;) {
- String key = (String)e.nextElement();
- h.put(key, get(key));
+ for (Map.Entry<Object, Object> e : entrySet()) {
+ String key = (String)e.getKey();
+ h.put(key, e.getValue());
}
}
/**
- * Enumerates all key/value pairs in the specified hashtable
+ * Enumerates all key/value pairs in the specified Map
* and omits the property if the key or value is not a string.
- * @param h the hashtable
+ * @param h the Map
*/
- private synchronized void enumerateStringProperties(Hashtable<String, String> h) {
+ private void enumerateStringProperties(Map<String, String> h) {
if (defaults != null) {
defaults.enumerateStringProperties(h);
}
- for (Enumeration<?> e = keys() ; e.hasMoreElements() ;) {
- Object k = e.nextElement();
- Object v = get(k);
+ for (Map.Entry<Object, Object> e : entrySet()) {
+ Object k = e.getKey();
+ Object v = e.getValue();
if (k instanceof String && v instanceof String) {
h.put((String) k, (String) v);
}
}
}
@@ -1132,6 +1139,231 @@
/** A table of hex digits */
private static final char[] hexDigit = {
'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
};
+
+ //
+ // Hashtable methods overridden and delegated to a ConcurrentHashMap instance
+
+ private transient ConcurrentHashMap<Object, Object> map =
+ new ConcurrentHashMap<>(8);
+
+ @Override
+ public int size() {
+ return map.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return map.isEmpty();
+ }
+
+ @Override
+ public Enumeration<Object> keys() {
+ return map.keys();
+ }
+
+ @Override
+ public Enumeration<Object> elements() {
+ return map.elements();
+ }
+
+ @Override
+ public boolean contains(Object value) {
+ return map.contains(value);
+ }
+
+ @Override
+ public boolean containsValue(Object value) {
+ return map.containsValue(value);
+ }
+
+ @Override
+ public boolean containsKey(Object key) {
+ return map.containsKey(key);
+ }
+
+ @Override
+ public Object get(Object key) {
+ return map.get(key);
+ }
+
+ @Override
+ public Object put(Object key, Object value) {
+ return map.put(key, value);
+ }
+
+ @Override
+ public Object remove(Object key) {
+ return map.remove(key);
+ }
+
+ @Override
+ public void putAll(Map<?, ?> t) {
+ map.putAll(t);
+ }
+
+ @Override
+ public void clear() {
+ map.clear();
+ }
+
+ @Override
+ public String toString() {
+ return map.toString();
+ }
+
+ @Override
+ public Set<Object> keySet() {
+ return map.keySet();
+ }
+
+ @Override
+ public Set<Map.Entry<Object, Object>> entrySet() {
+ return map.entrySet();
+ }
+
+ @Override
+ public Collection<Object> values() {
+ return map.values();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return map.equals(o);
+ }
+
+ @Override
+ public int hashCode() {
+ return map.hashCode();
+ }
+
+ @Override
+ public Object getOrDefault(Object key, Object defaultValue) {
+ return map.getOrDefault(key, defaultValue);
+ }
+
+ @Override
+ public void forEach(BiConsumer<? super Object, ? super Object> action) {
+ map.forEach(action);
+ }
+
+ @Override
+ public void replaceAll(BiFunction<? super Object, ? super Object, ?> function) {
+ map.replaceAll(function);
+ }
+
+ @Override
+ public Object putIfAbsent(Object key, Object value) {
+ return map.putIfAbsent(key, value);
+ }
+
+ @Override
+ public boolean remove(Object key, Object value) {
+ return map.remove(key, value);
+ }
+
+ @Override
+ public boolean replace(Object key, Object oldValue, Object newValue) {
+ return map.replace(key, oldValue, newValue);
+ }
+
+ @Override
+ public Object replace(Object key, Object value) {
+ return map.replace(key, value);
+ }
+
+ @Override
+ public Object computeIfAbsent(Object key,
+ Function<? super Object, ?> mappingFunction) {
+ return map.computeIfAbsent(key, mappingFunction);
+ }
+
+ @Override
+ public Object computeIfPresent(Object key,
+ BiFunction<? super Object, ? super Object, ?> remappingFunction) {
+ return map.computeIfPresent(key, remappingFunction);
+ }
+
+ @Override
+ public Object compute(Object key,
+ BiFunction<? super Object, ? super Object, ?> remappingFunction) {
+ return map.compute(key, remappingFunction);
+ }
+
+ @Override
+ public Object merge(Object key, Object value,
+ BiFunction<? super Object, ? super Object, ?> remappingFunction) {
+ return map.merge(key, value, remappingFunction);
+ }
+
+ //
+ // Special Hashtable methods
+
+ @Override
+ protected void rehash() {
+ // no-op
+ }
+
+ @Override
+ public Object clone() {
+ Properties clone = (Properties) cloneHashtable();
+ clone.map = new ConcurrentHashMap<>(map);
+ return clone;
+ }
+
+ //
+ // Hashtable serialization overrides
+ // (these should emit and consume Hashtable-compatible stream)
+
+ @Override
+ void writeHashtable(ObjectOutputStream s) throws IOException {
+ List<Object> entryStack = new ArrayList<>(map.size() * 2); // an estimate
+
+ for (Map.Entry<Object, Object> entry : map.entrySet()) {
+ entryStack.add(entry.getValue());
+ entryStack.add(entry.getKey());
+ }
+
+ // Write out the simulated threshold, loadfactor
+ float loadFactor = 0.75f;
+ int count = entryStack.size() / 2;
+ int length = (int)(count / loadFactor) + (count / 20) + 3;
+ if (length > count && (length & 1) == 0) {
+ length--;
+ }
+ synchronized (map) { // in case of multiple concurrent serializations
+ defaultWriteHashtable(s, length, loadFactor);
+ }
+
+ // Write out simulated length and real count of elements
+ s.writeInt(length);
+ s.writeInt(count);
+
+ // Write out the key/value objects from the stacked entries
+ for (int i = entryStack.size() - 1; i >= 0; i--) {
+ s.writeObject(entryStack.get(i));
+ }
+ }
+
+ @Override
+ void readHashtable(ObjectInputStream s) throws IOException,
+ ClassNotFoundException {
+ // Read in the threshold and loadfactor
+ s.defaultReadObject();
+
+ // Read the original length of the array and number of elements
+ int origlength = s.readInt();
+ int elements = s.readInt();
+
+ // create CHM of appropriate capacity
+ map = new ConcurrentHashMap<>(elements);
+
+ // Read all the key/value objects
+ for (; elements > 0; elements--) {
+ Object key = s.readObject();
+ Object value = s.readObject();
+ map.put(key, value);
+ }
+ }
}
< prev index next >