Как создать копию объекта java.util.Properties?

У меня есть следующее поле и конструктор:

private final Properties properties;

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

Хорошей практикой является копирование каждой изменчивой коллекции в конструкторе. Я хочу сделать мелкую независимую копию. Как я могу это достичь?

Моя первая идея заключалась в использовании метода putAll():

private final Properties properties = new Properties();

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

Есть ли более простой, более совершенный или более идиоматический способ сделать это? Может быть, есть некоторые утилиты для этого в Guava или Apache Commons?

Ответ 1

Использование putAll() прекрасно... если вам нужно остаться в Properties. Он работает в O(number of elements) и имеет очень мало накладных расходов. Единственное отличие, которое я бы порекомендовал, это держаться подальше от Properties по соображениям производительности, если вам это не нужно, потому что он наследуется от Hashtable. Кроме того, не используйте Properties потому что он на самом деле не соответствует ни одному интерфейсу, просто Dictionary который является абстрактным классом; это ограничит ваши возможности. Смотрите: что значит "программировать на интерфейс"?

Начиная с платформы Java 2 v1.2 этот класс был модифицирован для реализации интерфейса Map, что сделало его членом Java Collections Framework. В отличие от новых реализаций коллекции, Hashtable синхронизирован. Если поточно-ориентированная реализация не требуется, рекомендуется использовать HashMap вместо Hashtable. Если требуется высококонкурентная реализация, ориентированная на многопотоковое исполнение, то вместо Hashtable рекомендуется использовать ConcurrentHashMap.

Что бы вы ни делали, не используйте clone() , это небезопасно и не работает. См.: Java: Почему нельзя использовать clone() для защитного копирования?


Вы отредактировали свой вопрос, чтобы спросить о гуаве и apache-commons. Если это чисто защитная копия и она неизменна, я бы рекомендовал использовать Map<String, String> map = ImmutableMap.copyOf(properties). Примечание: опять же, здесь не используется фактический объект Properties потому что Hashtable не рекомендуется, если он вам не нужен. Из вики

Если вы не ожидаете изменить коллекцию или ожидаете, что коллекция останется постоянной, рекомендуется защищенно копировать ее в неизменяемую коллекцию.

Важное замечание: Каждая из реализаций неизменяемой коллекции Guava отклоняет нулевые значения. Мы провели исчерпывающее исследование внутренней базы кода Google, которая показала, что нулевые элементы были разрешены в коллекциях примерно в 5% случаев, а остальные 95% случаев лучше всего обслуживались с помощью быстрого отказа от нулевых значений. Если вам нужно использовать нулевые значения, рассмотрите возможность использования Collections.unmodifiableList и его друзей в реализации коллекции, которая разрешает нулевое значение. Более подробные предложения можно найти здесь.

Ответ 2

Свойства реализуют Cloneable, поэтому, если хотите, вы можете сделать следующее.

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

добавьте это в свой класс

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

Или, если вы беспокоитесь о том, что используете клон, ваш класс также реализует сериализуемое, поэтому вы можете это сделать.

import org.apache.commons.lang.SerializationUtils;

this.properties = SerializationUtils.clone(properties);

Права собственности

Cloneable

Ответ 3

Попробуйте следующее:

Properties newProps = new Properties();
properties.forEach((key, value) -> {
    newProps.setProperty((String) key, (String) value);
});

Ответ 4

Или вы можете просто сделать это "длинным" способом:

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

Итератор из одного и того же пакета java.util как свойства, поэтому никаких внешних зависимостей.

Если предупреждение компилятора о непроверенных типах вас беспокоит, вы можете просто изменить его (если ваши ключи свойств являются строками):

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

Ответ 5

Приведенная ниже программа копирует один файл свойств в другой и удаляет также дубликаты. Файл свойств источника может иметь повторяющиеся свойства, но он создаст новый файл без дублирующих свойств.

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();
        }
    }

}