23

I have the following field and constructor:

private final Properties properties;

public PropertiesExpander(Properties properties) {
    this.properties = properties;
}

The good practice is to make copy of every mutable collection in constructor. I want to make a shallow, independent copy. How can I achieve that?

My first idea was to use putAll() method:

private final Properties properties = new Properties();

public PropertiesExpander(Properties properties) {
    this.properties.putAll(properties);
}

Is there any simpler, more performant or more idiomatic way of doing that? Maybe there are some utils for that in Guava or Apache Commons?

durron597
  • 31,968
  • 17
  • 99
  • 158
Michal Kordas
  • 10,475
  • 7
  • 58
  • 103

6 Answers6

21

Using putAll() is great... if you need to stay with Properties. It runs in O(number of elements) and has very little overhead. The only difference I would recommend is to stay away from Properties for performance reasons unless you need it, because it inherits from Hashtable. Also, don't use Properties because it doesn't actually conform to any interface, just Dictionary which is an abstract class; this will limit your options. See: What does it mean to "program to an interface"?

As of the Java 2 platform v1.2, this class was retrofitted to implement the Map interface, making it a member of the Java Collections Framework. Unlike the new collection implementations, Hashtable is synchronized. If a thread-safe implementation is not needed, it is recommended to use HashMap in place of Hashtable. If a thread-safe highly-concurrent implementation is desired, then it is recommended to use ConcurrentHashMap in place of Hashtable.

Whatever you do, do not use clone(), it is not secure and not performant. See: Java: Why shouldn't clone() be used for defensive copying?


You edited your question to ask about Guava and apache-commons. If it's purely a defensive copy, and it's immutable, I would recommend using Map<String, String> map = ImmutableMap.copyOf(properties). Note: again, this doesn't use an actual Properties object because Hashtable is not recommended unless you need it. From the wiki

When you don't expect to modify a collection, or expect a collection to remain constant, it's a good practice to defensively copy it into an immutable collection.

Important: Each of the Guava immutable collection implementations rejects null values. We did an exhaustive study on Google's internal code base that indicated that null elements were allowed in collections about 5% of the time, and the other 95% of cases were best served by failing fast on nulls. If you need to use null values, consider using Collections.unmodifiableList and its friends on a collection implementation that permits null. More detailed suggestions can be found here.

Community
  • 1
  • 1
durron597
  • 31,968
  • 17
  • 99
  • 158
4

Properties implements Cloneable, so it looks like you could do the following if you wanted.

this.properties = (Properties) properties.clone();

add this to your class

protected Object clone() throws CloneNotSupportedException {
    return super.clone();
}

Or if you're worried about using clone, your class also implements serializable, so you could do this.

import org.apache.commons.lang.SerializationUtils;

this.properties = SerializationUtils.clone(properties);

Properies

Cloneable

Pumphouse
  • 2,013
  • 17
  • 26
4

Try this:

Properties newProps = new Properties();
properties.forEach((key, value) -> {
    newProps.setProperty((String) key, (String) value);
});
Govind Samrow
  • 9,981
  • 13
  • 53
  • 90
Pavel Evstigneev
  • 4,918
  • 31
  • 21
1

Or you can just do it the "long" way:

    Iterator i = properties.keySet().iterator();
    while(i.hasNext()){
        this.properties.put(i.next(), properties.get(i));
    }

Iterator is from the same java.util package as Properties, so no outside dependencies.

If the compiler warning about unchecked types bothers you, you can simply change it to (assuming your property keys are strings):

    Iterator<Object> i = properties.keySet().iterator();
    while(i.hasNext()){
        this.properties.put(i.next().toString(), properties.get(i));
    }
Scott Schechter
  • 609
  • 6
  • 7
1

Another way would be simply to

Properties newVersion = new Properties(originalVersion);

It will behave initially as a copy of the originalVersion, except when the key in the newVersion would be deleted, it would revert back to the value as stored in the originalVersion. In effect, the originalVersion behaves as a default key/value set.

YoYo
  • 9,157
  • 8
  • 57
  • 74
0

The below program copies the one property file to another and will remove the duplicates also. The source property file may have duplicate properties but it will create the new file without any duplicate properties.

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Properties;
import java.util.Set;

public class App2 {

    public static void main(String[] args) {

        try (InputStream input = new FileInputStream("config.properties")) {

            Properties prop = new Properties();

            // load a properties file
            prop.load(input);

            writeToNewProperties(prop);
        } catch (IOException ex) {
            ex.printStackTrace();
        }

    }

    private static void writeToNewProperties(Properties prop) {
        Properties outPutProp = new Properties();
        // get the property value and print it out
        Set<String> stringPropertyNames = prop.stringPropertyNames();
        for (String propertyName : stringPropertyNames) {
            outPutProp.put(propertyName, prop.get(propertyName));
        }

        try (OutputStream output = new FileOutputStream("out-config.properties")) {
            // save properties to project root folder
            outPutProp.store(output, null);
            System.out.println(outPutProp);
        } catch (IOException io) {
            io.printStackTrace();
        }
    }

}
Sudhakar
  • 3,104
  • 2
  • 27
  • 36