< 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 >