Conversion de java.util.Properties en HashMap <String, String>

Réponses:

86

C'est parce que Propertiess'étendHashtable<Object, Object> (qui, à son tour, met en œuvre Map<Object, Object>). Vous essayez de nourrir cela dans un fichier Map<String, String>. C'est donc incompatible.

Vous devez insérer les propriétés de chaîne une par une dans votre carte ...

Par exemple:

for (final String name: properties.stringPropertyNames())
    map.put(name, properties.getProperty(name));
fge
la source
1
Oui, mais ce n'est pas le problème ici: les arguments génériques ne correspondent pas. Vous pouvez alimenter ce que vous voulez dans un Hashtable<Object, Object>, même des choses qui ne sont pas des chaînes - même des clés qui ne sont pas des chaînes.
fge
@assylias: Non, cela ne compilera pas non plus.
Jon Skeet
13
dans 1,8, vous pouvez faire properties.forEach ((k, v) -> map.put ((String) k, (String) v));
ModdyFire
1
Ou si vous n'avez pas déjà la carte en main properties.entrySet (). Stream (). Collect (Collectors.toMap (e -> (String) e.getKey (), e -> (String) e.getValue ( )))
Tonsic
46

Le moyen efficace de le faire est simplement de convertir en une carte générique comme suit:

Properties props = new Properties();

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

Cela convertira a Map<Object, Object>en une map brute, ce qui est "ok" pour le compilateur (seul avertissement). Une fois que nous avons un raw, Mapil sera casté dans Map<String, String>lequel il sera également "ok" (un autre avertissement). Vous pouvez les ignorer avec une annotation@SuppressWarnings({ "unchecked", "rawtypes" })

Cela fonctionnera car dans la JVM, l'objet n'a pas vraiment de type générique. Les types génériques ne sont qu'une astuce qui vérifie les choses au moment de la compilation.

Si une clé ou une valeur n'est pas une chaîne, cela produira une ClassCastExceptionerreur. Avec l' Propertiesimplémentation actuelle, il est très peu probable que cela se produise, tant que vous n'utilisez pas les méthodes d'appel mutables du super Hashtable<Object,Object>de Properties.

Donc, si vous ne faites pas de mauvaises choses avec votre instance Properties, c'est la voie à suivre.

Padilo
la source
La question est de convertir en HashMap. Pas n'importe quelle carte.
AlikElzin-kilaka
3
Oui, le titre de la question le dit, mais le but est d'avoir une Mapinstance au moins au code donné, alors j'ai pensé que c'était ce dont il avait besoin
padilo
Bien que j'aime les autres solutions puristes, cette solution m'est utile car ce n'est qu'une simple ligne.
Alfonso Nishikawa
27

Que dis-tu de ça?

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

Provoque un avertissement, mais fonctionne sans itérations.

Seshadri Sastry
la source
4
@fge: Ce n'est pas un Map<Object, Object>, c'est un Mapargument (type brut). Cette réponse est correcte
Lukas Eder
2
Euh, oui j'ai essayé avec Eclipse. Une de ces différences génériques entre Eclipse et javac à nouveau? .... non, fonctionne également avec javac
Lukas Eder
4
Cela fonctionne mais l'itération se produit toujours. Si vous regardez le code source de HashMap, le constructeur itère essentiellement à travers le paramètre de carte générique. Le temps de calcul ne change donc pas mais le code est certainement plus concis.
Simeon G
Comme indiqué dans la réponse précédente, il n'est pas nécessaire de créer une nouvelle instance et d'itérer sur l'objet de propriétés. Utilisez simplement une séquence de lancers: (Map<String, String>) ((Map) properties)
Ricardo Veloso
22

La manière Java 8:

properties.entrySet().stream().collect(
    Collectors.toMap(
         e -> e.getKey().toString(),
         e -> e.getValue().toString()
    )
);
Ben McCann
la source
Existe-t-il un moyen d'utiliser la référence de méthode au lieu de lambda. Cos des problèmes de sonar qube.
Viyaan Jhiingade
16

Propertiesimplémente Map<Object, Object>- non Map<String, String>.

Vous essayez d'appeler ce constructeur:

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

... avec Ket les Vdeux comme String.

Mais Map<Object, Object>n'est pas un Map<? extends String, ? extends String>... il peut contenir des clés et des valeurs sans chaîne.

Cela fonctionnerait:

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

... mais cela ne vous serait pas aussi utile.

Fondamentalement, Propertiesje n'aurais jamais dû faire une sous-classe de HashTable... c'est le problème. Depuis la v1, il a toujours été en mesure de stocker des clés et des valeurs non-String, bien que cela soit contraire à l'intention. Si la composition avait été utilisé à la place, l'API aurait pu ne travaillé avec les clés de chaîne / valeurs, et tout aurait été bien.

Vous voudrez peut-être quelque chose comme ceci:

Map<String, String> map = new HashMap<String, String>();
for (String key : properties.stringPropertyNames()) {
    map.put(key, properties.getProperty(key));
}
Jon Skeet
la source
apparemment, il n'est pas non plus possible de le faire explicitement Properties<String,String> properties = new Properties<String,String>();. Particulier.
eis le
1
@eis C'est par conception, en Propertiessoi n'est pas générique.
Mattias Buelens
Je préfère dire que c'est par série de choix malheureux plutôt que par conception, mais oui.
eis le
2
@eis: Non, c'est par conception que Properties est censé être une carte chaîne à chaîne. Il est logique que ce ne soit pas générique. Cela n'a aucun sens que vous puissiez ajouter des clés / valeurs sans chaîne.
Jon Skeet
8

Si vous savez que votre Propertiesobjet ne contient que des <String, String>entrées, vous pouvez recourir à un type brut:

Properties properties = new Properties();
Map<String, String> map = new HashMap<String, String>((Map) properties);
Lukas Eder
la source
4

Le problème est que Propertiesimplémente Map<Object, Object>, alors que le HashMapconstructeur attend un Map<? extends String, ? extends String>.

Cette réponse explique cette décision (assez contre-intuitive). En bref: avant Java 5, Propertiesimplémenté Map(car il n'y avait pas de génériques à l'époque). Cela signifie que vous pouvez mettre tout Object dans un Propertiesobjet. Ceci est toujours dans la documentation:

Comme Propertieshérite de Hashtable, les méthodes putet putAllpeuvent être appliquées à un Propertiesobjet. Leur utilisation est fortement déconseillée car ils permettent à l'appelant d'insérer des entrées dont les clés ou les valeurs ne sont pas des Strings. La setPropertyméthode doit être utilisée à la place.

Pour maintenir la compatibilité avec cela, les concepteurs n'avaient d'autre choix que de le faire hériter Map<Object, Object>de Java 5. C'est un résultat malheureux de l'effort de compatibilité descendante totale qui rend le nouveau code inutilement alambiqué.

Si vous n'utilisez que des propriétés de chaîne dans votre Propertiesobjet, vous devriez pouvoir vous en sortir avec un cast non coché dans votre constructeur:

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

ou sans aucune copie:

Map<String, String> map = (Map<String, String>) properties;
Mattias Buelens
la source
C'est la signature du constructeur HashMap public HashMap(Map<? extends K, ? extends V> m). Il ne s'attend pas à unMap<String, String>
Mubin
@Mubin D'accord, j'ai un peu simplifié la question. Pourtant, l'argument est Map<Object, Object>vrai : a ne peut pas être utilisé pour un argument formel de type` Map <? étend String,? étend la chaîne> `.
Mattias Buelens
2

c'est uniquement parce que le constructeur de HashMap nécessite un argument de type générique Map et que Properties implémente Map.

Cela fonctionnera, mais avec un avertissement

    Properties properties = new Properties();
    Map<String, String> map = new HashMap(properties);
Evgeniy Dorofeev
la source
1

Vous pouvez utiliser ceci:

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

props.forEach((key, value) -> map.put(key.toString(), value.toString()));
Raman Sahasi
la source
0

Première chose,

La classe Properties est basée sur Hashtable et non sur Hashmap. La classe Properties étend fondamentalement Hashtable

Il n'y a pas de constructeur de ce type dans la classe HashMap qui prend un objet properties et vous renvoie un objet hashmap. Donc ce que vous faites n'est PAS correct. Vous devriez pouvoir convertir l'objet de propriétés en référence de table de hachage.

Juned Ahsan
la source
0

j'utilise ceci:

for (Map.Entry<Object, Object> entry:properties.entrySet()) {
    map.put((String) entry.getKey(), (String) entry.getValue());
}
atome
la source