Преобразование java.util.Properties в HashMap <String, String>

Ответы:

86

Это потому, что PropertiesextendsHashtable<Object, Object> (который, в свою очередь, реализует Map<Object, Object>). Вы пытаетесь скормить этоMap<String, String> . Следовательно, это несовместимо.

Вам нужно передать строковые свойства одно за другим в вашу карту ...

Например:

for (final String name: properties.stringPropertyNames())
    map.put(name, properties.getProperty(name));
fge
источник
1
Да, но проблема не в этом: общие аргументы не совпадают. Вы можете кормить в a все, что захотите Hashtable<Object, Object>, даже вещи, которые не являются строками - даже ключи, которые не являются строками.
fge
@assylias: Нет, это тоже не скомпилируется.
Джон Скит
13
в 1,8 вы можете сделать properties.forEach ((k, v) -> map.put ((String) k, (String) v));
ModdyFire
1
Или, если у вас еще нет карты под рукой, properties.entrySet (). Stream (). Collect (Collectors.toMap (e -> (String) e.getKey (), e -> (String) e.getValue ( )))
Tonsic
46

Эффективный способ сделать это - просто привести к общей карте следующим образом:

Properties props = new Properties();

Map<String, String> map = (Map)props;

Это преобразует a Map<Object, Object>в необработанную карту, что "нормально" для компилятора (только предупреждение). Как только у нас будет необработанный файл, Mapон будет преобразован вMap<String, String> он также будет «в порядке» (еще одно предупреждение). Вы можете игнорировать их с помощью аннотации@SuppressWarnings({ "unchecked", "rawtypes" })

Это будет работать, потому что в JVM объект действительно не имеет универсального типа. Универсальные типы - это всего лишь уловка, которая проверяет вещи во время компиляции.

Если какой-либо ключ или значение не является строкой, это приведет к ClassCastExceptionошибке. При текущей Propertiesреализации это очень маловероятно , чтобы это произошло, до тех пор , пока вы не использовать методы изменяемые вызовов из супер Hashtable<Object,Object>вProperties .

Итак, если вы не делаете неприятных вещей с вашим экземпляром Properties, это правильный путь.

Padilo
источник
Вопрос в том, чтобы конвертировать в HashMap. Никакой карты.
AlikElzin-kilaka
3
Да, это сказано в заголовке вопроса, но цель состоит в том, чтобы иметь Mapэкземпляр хотя бы с заданным кодом, поэтому я подумал, что это то, что ему нужно,
Падило
Хотя мне нравятся другие решения пуристов, это решение пригодится мне, поскольку это всего лишь одна простая линия.
Альфонсо Нисикава
27

Как насчет этого?

   Map properties = new Properties();
   Map<String, String> map = new HashMap<String, String>(properties);

Вызовет предупреждение, но работает без повторов.

Сешадри Шастри
источник
4
@fge: это не a Map<Object, Object>, это Mapаргумент (необработанный тип). Ответ правильный
Лукас Эдер
2
Да, я пробовал с Eclipse. Опять одно из тех общих различий между Eclipse и javac? .... нет, работает и с javac
Лукас Эдер
4
Это работает, но итерация все еще происходит. Если вы посмотрите на исходный код HashMap, конструктор в основном выполняет итерацию по универсальному параметру карты. Таким образом, время вычислений не меняется, но код, безусловно, более лаконичный.
Simeon G
Как было сказано в предыдущем ответе, нет необходимости создавать новый экземпляр и перебирать объект свойств. Просто используйте последовательность бросков: (Map<String, String>) ((Map) properties)
Рикардо Велозу
22

Способ Java 8:

properties.entrySet().stream().collect(
    Collectors.toMap(
         e -> e.getKey().toString(),
         e -> e.getValue().toString()
    )
);
Бен Макканн
источник
Есть ли способ использовать ссылку на метод вместо лямбда. Из-за проблем с сонаром.
Viyaan Jhiingade
16

Propertiesорудия Map<Object, Object>- нетMap<String, String> .

Вы пытаетесь вызвать этот конструктор:

public HashMap(Map<? extends K,? extends V> m)

... с Kи Vобоими какString .

Но Map<Object, Object>это неMap<? extends String, ? extends String> ... он может содержать нестроковые ключи и значения.

Это сработает:

Map<Object, Object> map = new HashMap<Object, Object>();

... но это было бы не так полезно для вас.

По сути, Propertiesникогда не следовало делать подклассом HashTable... вот в чем проблема. Начиная с версии 1, он всегда мог хранить ключи и значения, отличные от String, несмотря на то, что это противоречило намерениям. Если бы вместо этого использовалась композиция, API мог бы иметь только работать со строковыми ключами / значениями, и все было бы хорошо.

Вам может понадобиться что-то вроде этого:

Map<String, String> map = new HashMap<String, String>();
for (String key : properties.stringPropertyNames()) {
    map.put(key, properties.getProperty(key));
}
Джон Скит
источник
видимо, это тоже нельзя явно сделать Properties<String,String> properties = new Properties<String,String>();. Своеобразный.
eis
1
@eis Это по дизайну, Propertiesсамо по себе не является общим.
Mattias Buelens
Я бы сказал, что это результат неудачного выбора, а не дизайн, но да.
eis
2
@eis: Нет, свойство Properties задумано как преобразование строки в строку. Имеет смысл, что это не универсальный. Это не имеет смысла , что вы можете добавить нестроковые ключей / значений.
Джон Скит,
8

если ты знаете, что ваш Propertiesобъект содержит только <String, String>записи, вы можете прибегнуть к необработанному типу:

Properties properties = new Properties();
Map<String, String> map = new HashMap<String, String>((Map) properties);
Лукас Эдер
источник
4

Проблема в том, что Propertiesреализует Map<Object, Object>, тогда какHashMap конструктор ожидает Map<? extends String, ? extends String>.

Этот ответ объясняет это (довольно противоречащее интуиции) решение. Вкратце: до Java 5 Propertiesреализовано Map(поскольку тогда не было дженериков). Это означало , что вы могли бы поместить любой Object в Propertiesобъекте.Это все еще есть в документации:

Поскольку Propertiesнаследует от Hashtable, как putи putAllметоды могут быть применены к Propertiesобъекту. Их использование настоятельно не рекомендуется, поскольку они позволяют вызывающей стороне вставлять записи, ключи или значения которых не являются Strings. ВsetPropertyМетод должен быть использован вместо.

Для обеспечения совместимости с этим у дизайнеров не было другого выбора, кроме как заставить его наследовать Map<Object, Object> Java 5. Это досадный результат стремления к полной обратной совместимости, делающей новый код излишне запутанным.

Если вы только когда - либо использовать строковые свойства в вашем Propertiesобъекте, вы должны быть в состоянии уйти с непроверенным отлиты в конструкторе:

Map<String, String> map = new HashMap<String, String>( (Map<String, String>) properties);

или без копий:

Map<String, String> map = (Map<String, String>) properties;
Маттиас Буэленс
источник
Это подпись конструктора HashMap public HashMap(Map<? extends K, ? extends V> m). Он не ожидаетMap<String, String>
Мубин
@Mubin Ладно, я немного упростил дело. Тем не менее, аргумент Map<Object, Object>остается в силе : a нельзя использовать для формального аргумента типа `Map <? расширяет String,? расширяет String> `.
Mattias Buelens
2

это только потому, что конструктору HashMap требуется аргумент универсального типа Map, а Properties реализует Map.

Это будет работать, но с предупреждением

    Properties properties = new Properties();
    Map<String, String> map = new HashMap(properties);
Евгений Дорофеев
источник
1

Вы можете использовать это:

Map<String, String> map = new HashMap<>();

props.forEach((key, value) -> map.put(key.toString(), value.toString()));
Раман Сахаси
источник
0

Первым делом,

Класс свойств основан на Hashtable, а не на Hashmap. Класс свойств в основном расширяет Hashtable

В классе HashMap нет такого конструктора, который принимает объект свойств и возвращает вам объект hashmap. Так что то, что вы делаете, НЕ правильно. Вы должны иметь возможность привести объект свойств к ссылке на хеш-таблицу.

Джунед Ахсан
источник
0

я использую это:

for (Map.Entry<Object, Object> entry:properties.entrySet()) {
    map.put((String) entry.getKey(), (String) entry.getValue());
}
атом
источник