Conversión de java.util.Properties a HashMap <String, String>

Respuestas:

86

Esto se debe a que PropertiesextiendeHashtable<Object, Object> (que, a su vez, implementa Map<Object, Object>). Intenta alimentar eso en un Map<String, String>. Por tanto, es incompatible.

Necesita introducir las propiedades de la cadena una por una en su mapa ...

Por ejemplo:

for (final String name: properties.stringPropertyNames())
    map.put(name, properties.getProperty(name));
fge
fuente
1
Sí, pero ese no es el problema aquí: los argumentos genéricos no coinciden. Puede alimentar lo que quiera en un Hashtable<Object, Object>, incluso cosas que no son cadenas, incluso teclas que no son cadenas.
fge
@assylias: No, eso tampoco se compilará.
Jon Skeet
13
en 1,8 puede hacer properties.forEach ((k, v) -> map.put ((String) k, (String) v));
ModdyFire
1
O si aún no tiene el mapa en mano properties.entrySet (). Stream (). Collect (Collectors.toMap (e -> (String) e.getKey (), e -> (String) e.getValue ( )))
Tonsic
46

La forma eficiente de hacerlo es simplemente convertir a un mapa genérico de la siguiente manera:

Properties props = new Properties();

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

Esto convertirá un Map<Object, Object>mapa en un mapa sin formato, que está "bien" para el compilador (solo advertencia). Una vez que tengamos un raw Mapse lanzará al Map<String, String>que también estará "ok" (otra advertencia). Puedes ignorarlos con una anotación.@SuppressWarnings({ "unchecked", "rawtypes" })

Esto funcionará porque en la JVM el objeto realmente no tiene un tipo genérico. Los tipos genéricos son solo un truco que verifica las cosas en tiempo de compilación.

Si alguna clave o valor no es una cadena, producirá un ClassCastExceptionerror. Con la Propertiesimplementación actual , es muy poco probable que esto suceda, siempre y cuando no use los métodos de llamada mutables del super Hashtable<Object,Object>de Properties.

Entonces, si no hace cosas desagradables con su instancia de Propiedades, este es el camino a seguir.

padilo
fuente
La pregunta es convertir a HashMap. No cualquier mapa.
AlikElzin-kilaka
3
Sí, el título de la pregunta dice eso, pero el objetivo es tener una Mapinstancia al menos en el código dado, así que pensé que esto es lo que necesita
padilo
Aunque me gustan otras soluciones puristas, esta solución me resulta muy útil ya que es solo una línea simple.
Alfonso Nishikawa
27

¿Qué tal esto?

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

Causará una advertencia, pero funciona sin iteraciones.

Seshadri Sastry
fuente
4
@fge: No es un Map<Object, Object>, es un Mapargumento (tipo sin formato ). Esta respuesta es correcta
Lukas Eder
2
Eh, sí, lo intenté con Eclipse. ¿Otra de esas diferencias genéricas entre Eclipse y javac? .... no, también funciona con javac
Lukas Eder
4
Esto funciona, pero la iteración aún ocurre. Si observa el código fuente de HashMap, el constructor básicamente itera a través del parámetro de mapa genérico. Por tanto, el tiempo de cálculo no cambia, pero el código es ciertamente más conciso.
Simeon G
Como se indicó en la respuesta anterior, no es necesario crear una nueva instancia e iterar sobre el objeto de propiedades. Solo use una secuencia de yesos: (Map<String, String>) ((Map) properties)
Ricardo Veloso
22

La forma Java 8:

properties.entrySet().stream().collect(
    Collectors.toMap(
         e -> e.getKey().toString(),
         e -> e.getValue().toString()
    )
);
Ben McCann
fuente
¿Hay alguna forma de usar la referencia de método en lugar de lambda? Cos de problemas de qube sonar.
Viyaan Jhiingade
16

Propertiesimplementos Map<Object, Object>- no Map<String, String>.

Estás intentando llamar a este constructor:

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

... con Ky Vambos como String.

Pero Map<Object, Object>no es un Map<? extends String, ? extends String>... puede contener claves y valores que no sean cadenas.

Esto funcionaría:

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

... pero no te sería tan útil.

Fundamentalmente, Propertiesnunca debería haber sido una subclase de HashTable... ese es el problema. Desde v1, siempre ha sido posible almacenar claves y valores que no son de cadena, a pesar de que eso va en contra de la intención. Si se hubiera utilizado la composición en su lugar, la API solo podría haber funcionado con claves / valores de cadena, y todo habría ido bien.

Es posible que desee algo como esto:

Map<String, String> map = new HashMap<String, String>();
for (String key : properties.stringPropertyNames()) {
    map.put(key, properties.getProperty(key));
}
Jon Skeet
fuente
aparentemente tampoco es posible hacerlo explícitamente Properties<String,String> properties = new Properties<String,String>();. Peculiar.
EIS
1
@eis Eso es por diseño, en Propertiessí mismo no es genérico.
Mattias Buelens
Prefiero decir que es por una serie de decisiones desafortunadas en lugar de por diseño, pero sí.
EIS
2
@eis: No, es por diseño que Propiedades está destinado a ser un mapa de cadena a cadena. Tiene sentido que no sea genérico. No tiene sentido que pueda agregar claves / valores que no sean cadenas.
Jon Skeet
8

Si sabe que su Propertiesobjeto solo contiene <String, String>entradas, puede recurrir a un tipo sin formato:

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

El problema es que Propertiesimplementa Map<Object, Object>, mientras que el HashMapconstructor espera un Map<? extends String, ? extends String>.

Esta respuesta explica esta decisión (bastante contraria a la intuición). En resumen: antes de Java 5, Propertiesimplementado Map(ya que no había genéricos en ese entonces). Esto significaba que podía poner cualquiera Object en un Propertiesobjeto. Esto todavía está en la documentación:

Dado que Propertieshereda de Hashtable, los métodos puty putAllse pueden aplicar a un Propertiesobjeto. Se desaconseja encarecidamente su uso, ya que permiten a la persona que llama insertar entradas cuyas claves o valores no son Strings. En su lugar setProperty, debería utilizarse el método.

Para mantener la compatibilidad con esto, los diseñadores no tuvieron otra opción que heredarlo Map<Object, Object>en Java 5. Es un resultado desafortunado del esfuerzo por lograr una compatibilidad total con versiones anteriores que hace que el nuevo código sea innecesariamente complicado.

Si solo usa propiedades de cadena en su Propertiesobjeto, debería poder salirse con una conversión sin marcar en su constructor:

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

o sin copias:

Map<String, String> map = (Map<String, String>) properties;
Mattias Buelens
fuente
Esta es la firma del constructor HashMap public HashMap(Map<? extends K, ? extends V> m). No espera unMap<String, String>
Mubin
@Mubin Está bien, simplifiqué un poco el asunto. Aún así, el argumento se mantiene: a Map<Object, Object>no se puede usar para un argumento formal de tipo` Map <? extiende String,? extiende Cadena> `.
Mattias Buelens
2

esto es solo porque el constructor de HashMap requiere un argumento de tipo Map genérico y Propiedades implementa Map.

Esto funcionará, aunque con una advertencia

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

Puedes usar esto:

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

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

Lo primero,

La clase de propiedades se basa en Hashtable y no en Hashmap. La clase de propiedades básicamente extiende Hashtable

No existe tal constructor en la clase HashMap que toma un objeto de propiedades y le devuelve un objeto hashmap. Entonces, lo que estás haciendo NO es correcto. Debería poder convertir el objeto de propiedades en una referencia de tabla hash.

Juned Ahsan
fuente
0

yo uso esto:

for (Map.Entry<Object, Object> entry:properties.entrySet()) {
    map.put((String) entry.getKey(), (String) entry.getValue());
}
átomo
fuente