Konwersja java.util.Properties do HashMap <String, String>


Odpowiedzi:


86

Dzieje się tak, ponieważ PropertiesrozszerzaHashtable<Object, Object> (który z kolei realizuje Map<Object, Object>). Próbujesz wprowadzić to do pliku Map<String, String>. Dlatego jest nie do pogodzenia.

Musisz wprowadzić właściwości ciągów jeden po drugim do swojej mapy ...

Na przykład:

for (final String name: properties.stringPropertyNames())
    map.put(name, properties.getProperty(name));

1
Tak, ale to nie jest problem: ogólne argumenty nie są zgodne. Możesz podawać cokolwiek chcesz Hashtable<Object, Object>, nawet rzeczy, które nie są łańcuchami - nawet klucze, które nie są łańcuchami.
fge

@assylias: Nie, to też się nie skompiluje.
Jon Skeet

13
w 1,8 można zrobić properties.forEach ((k, v) -> map.put ((String) k, (String) v));
ModdyFire

1
Lub jeśli nie masz jeszcze mapy w ręku properties.entrySet (). Stream (). Collect (Collectors.toMap (e -> (String) e.getKey (), e -> (String) e.getValue ( )))
Tonsic

46

Skutecznym sposobem jest po prostu rzutowanie do ogólnej mapy w następujący sposób:

Properties props = new Properties();

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

Spowoduje to konwersję Map<Object, Object>do surowej mapy, co jest „w porządku” dla kompilatora (tylko ostrzeżenie). Kiedy już mamy nieprzetworzony plik Map, zostanie on rzucony, na Map<String, String>który również będzie "ok" (kolejne ostrzeżenie). Możesz je zignorować za pomocą adnotacji@SuppressWarnings({ "unchecked", "rawtypes" })

To zadziała, ponieważ w JVM obiekt tak naprawdę nie ma typu ogólnego. Typy ogólne to tylko sztuczka, która weryfikuje rzeczy w czasie kompilacji.

Jeśli jakiś klucz lub wartość nie jest ciągiem, spowoduje to ClassCastExceptionbłąd. Przy obecnej Propertiesimplementacji jest to bardzo mało prawdopodobne, o ile nie używasz mutowalnych metod wywołania z super Hashtable<Object,Object>of Properties.

Tak więc, jeśli nie robisz nieprzyjemnych rzeczy z instancją Właściwości, jest to właściwa droga.


Chodzi o to, aby przekonwertować na HashMap. Nie ma żadnej mapy.
AlikElzin-kilaka

3
Tak, tytuł pytania tak mówi, ale celem jest mieć Mapinstancję przynajmniej na podanym kodzie, więc pomyślałem, że tego właśnie potrzebuje
padilo

Chociaż lubię inne purystyczne rozwiązania, to rozwiązanie mi się przydaje, bo to tylko jedna prosta linia.
Alfonso Nishikawa


27

Co powiesz na to?

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

Powoduje ostrzeżenie, ale działa bez iteracji.


4
@fge: To nie jest Map<Object, Object>, to Mapargument (typ surowy). Ta odpowiedź jest prawidłowa
Lukas Eder

2
Huh, tak, próbowałem z Eclipse. Znowu jedna z tych ogólnych różnic między Eclipse i javac? .... nie, współpracuje również z javac
Lukas Eder

4
To działa, ale iteracja nadal ma miejsce. Jeśli spojrzysz na kod źródłowy HashMap, konstruktor w zasadzie wykonuje iterację przez ogólny parametr mapy. Więc czas obliczeń się nie zmienia, ale kod jest z pewnością bardziej zwięzły.
Simeon G

Jak stwierdzono w poprzedniej odpowiedzi, nie ma potrzeby tworzenia nowej instancji i iteracji po obiekcie właściwości. Po prostu użyj sekwencji rzutów: (Map<String, String>) ((Map) properties)
Ricardo Veloso.

22

Sposób Java 8:

properties.entrySet().stream().collect(
    Collectors.toMap(
         e -> e.getKey().toString(),
         e -> e.getValue().toString()
    )
);

Czy istnieje sposób na użycie odwołania do metody zamiast lambda. Ze względu na problemy z kostką sonaru.
Viyaan Jhiingade

16

Propertiesnarzędzia Map<Object, Object>- nieMap<String, String> .

Próbujesz wywołać tego konstruktora:

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

... ze Ki Vzarówno jakoString .

Ale Map<Object, Object>nie jestMap<? extends String, ? extends String> ... może zawierać klucze i wartości niebędące łańcuchami.

To zadziała:

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

... ale nie byłoby to dla ciebie tak przydatne.

Zasadniczo Propertiesnigdy nie powinno się tworzyć podklasy HashTable… w tym tkwi problem. Od wersji 1 zawsze był w stanie przechowywać klucze i wartości inne niż łańcuchowe, mimo że było to sprzeczne z intencją. Gdyby zamiast tego użyto kompozycji, API mogłoby mieć tylko z kluczami / wartościami w postaci ciągów i wszystko byłoby dobrze.

Możesz chcieć czegoś takiego:

Map<String, String> map = new HashMap<String, String>();
for (String key : properties.stringPropertyNames()) {
    map.put(key, properties.getProperty(key));
}

najwyraźniej nie jest to również możliwe w sposób wyraźny Properties<String,String> properties = new Properties<String,String>();. Szczególny.
eis

1
@eis Tak jest z założenia, Propertiessamo w sobie nie jest ogólne.
Mattias Buelens

Wolałbym raczej powiedzieć, że wynika to z niefortunnej serii wyborów, a nie z projektu, ale tak.
eis

2
@eis: Nie, to zgodnie z projektem właściwości mają być mapą typu łańcuch-ciąg. Ma sens, że nie jest to ogólne. Nie ma sensu dodawanie kluczy / wartości niebędących ciągami.
Jon Skeet,


8

Jeśli ty wiesz, że twój Propertiesobiekt zawiera tylko <String, String>wpisy, możesz użyć surowego typu:

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

4

Problem polega na tym , że Propertiesimplementuje Map<Object, Object>, podczas gdy HashMapkonstruktor oczekuje plikuMap<? extends String, ? extends String> .

Ta odpowiedź wyjaśnia tę (dość sprzeczną z intuicją) decyzję. W skrócie: Propertieszaimplementowano przed Javą 5 Map(bo wtedy nie było generycznych). Oznaczało to, że można umieścić dowolny Object w Propertiesobiekt. To jest nadal w dokumentacji:

Ponieważ Propertiesdziedziczy po Hashtable, metody puti putAllmożna zastosować do Propertiesobiektu. Ich użycie jest zdecydowanie odradzane, ponieważ pozwalają wywołującemu wstawiać wpisy, których klucze lub wartości nie są Strings. PliksetPropertySposób powinien być używany.

Aby zachować zgodność z tym, projektanci nie mieli innego wyjścia, jak tylko odziedziczyć Map<Object, Object> w Javie 5. Jest to niefortunny rezultat dążenia do pełnej kompatybilności wstecznej, który powoduje niepotrzebne zagmatwanie nowego kodu.

Jeśli kiedykolwiek użyjesz tylko właściwości ciągu w swoim Propertiesobiekcie, powinieneś być w stanie uciec z niesprawdzonym rzutowaniem w konstruktorze:

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

lub bez kopii:

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

To jest sygnatura konstruktora HashMap public HashMap(Map<? extends K, ? extends V> m). Nie oczekujeMap<String, String>
Mubin

@Mubin OK, trochę uprościłem sprawę. Mimo to argument utrzymuje: a Map<Object, Object>nie można użyć jako argumentu formalnego typu `Map <? rozszerza String,? rozszerza String> `.
Mattias Buelens

2

Dzieje się tak tylko dlatego, że konstruktor HashMap wymaga argumentu typu generycznego Map, a właściwości implementują Map.

To zadziała, choć z ostrzeżeniem

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

1

Możesz użyć tego:

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

props.forEach((key, value) -> map.put(key.toString(), value.toString()));

0

Pierwsza rzecz,

Klasa Properties jest oparta na Hashtable, a nie Hashmap. Klasa Properties zasadniczo rozszerza Hashtable

W klasie HashMap nie ma takiego konstruktora, który pobierałby obiekt właściwości i zwracałby obiekt hashmap. Więc to, co robisz, NIE jest poprawne. Powinieneś być w stanie rzutować obiekt właściwości na odniesienie z hashtagiem.


0

używam tego:

for (Map.Entry<Object, Object> entry:properties.entrySet()) {
    map.put((String) entry.getKey(), (String) entry.getValue());
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.