Convertendo java.util.Properties em HashMap <String, String>


Respostas:


89

Isso ocorre porque PropertiesestendeHashtable<Object, Object> (que, por sua vez, implementa Map<Object, Object>). Você tenta alimentar isso em um Map<String, String>. Portanto, é incompatível.

Você precisa alimentar as propriedades da string, uma por uma, em seu mapa ...

Por exemplo:

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

1
Sim, mas esse não é o problema aqui: os argumentos genéricos não coincidem. Você pode alimentar o que quiser em um Hashtable<Object, Object>, até mesmo coisas que não sejam strings - até mesmo chaves que não sejam strings.
fge

@assylias: Não, isso também não vai compilar.
Jon Skeet de

13
em 1,8 você pode fazer properties.forEach ((k, v) -> map.put ((String) k, (String) v));
ModdyFire de

1
Ou se você ainda não tiver o mapa em mãos, properties.entrySet (). Stream (). Collect (Collectors.toMap (e -> (String) e.getKey (), e -> (String) e.getValue ( )))
Tonsic

47

A maneira eficiente de fazer isso é lançar para um mapa genérico da seguinte maneira:

Properties props = new Properties();

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

Isso converterá um Map<Object, Object>em um mapa bruto, que é "ok" para o compilador (apenas aviso). Assim que tivermos um raw, Mapele será lançado para o Map<String, String>qual também estará "ok" (outro aviso). Você pode ignorá-los com anotações@SuppressWarnings({ "unchecked", "rawtypes" })

Isso funcionará porque na JVM o objeto não tem realmente um tipo genérico. Tipos genéricos são apenas um truque que verifica as coisas em tempo de compilação.

Se alguma chave ou valor não for uma String, ocorrerá um ClassCastExceptionerro. Com a Propertiesimplementação atual, é muito improvável que isso aconteça, contanto que você não use os métodos de chamada mutáveis ​​do super Hashtable<Object,Object>de Properties.

Portanto, se não fizer coisas desagradáveis ​​com sua instância de Propriedades, este é o caminho a seguir.


A questão é converter para HashMap. Nenhum mapa.
AlikElzin-kilaka

3
Sim, o título da pergunta diz isso, mas o objetivo é ter uma Mapinstância pelo menos no código fornecido, então pensei que isso é o que ele precisa
padilo

Embora eu goste de outras soluções puristas, essa solução é útil para mim, pois é apenas uma linha simples.
Alfonso Nishikawa


28

Que tal agora?

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

Irá causar um aviso, mas funciona sem iterações.


4
@fge: Não é um Map<Object, Object>, é um Mapargumento (tipo bruto). Esta resposta está correta
Lukas Eder

2
Huh, sim, eu tentei com Eclipse. Uma dessas diferenças genéricas entre Eclipse e javac de novo? .... não, funciona com javac também
Lukas Eder

4
Isso funciona, mas a iteração ainda acontece. Se você olhar o código-fonte do HashMap, o construtor basicamente itera por meio do parâmetro de mapa genérico. Portanto, o tempo de cálculo não muda, mas o código é certamente mais conciso.
Simeon G

Como a resposta anterior afirmou, não há necessidade de criar uma nova instância e iterar sobre o objeto de propriedades. Basta usar uma sequência de elencos: (Map<String, String>) ((Map) properties)
Ricardo Veloso

23

O método Java 8:

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

Existe uma maneira de usar a referência de método em vez de lambda. Por causa dos problemas do sonar.
Viyaan Jhiingade

17

Propertiesimplementos Map<Object, Object>- não Map<String, String>.

Você está tentando chamar este construtor:

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

... com Ke Vambos como String.

Mas Map<Object, Object>não é Map<? extends String, ? extends String>... pode conter chaves e valores que não sejam de string.

Isso funcionaria:

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

... mas não seria tão útil para você.

Fundamentalmente, Propertiesnunca deveria ter sido feita uma subclasse de HashTable... esse é o problema. Desde v1, ele sempre foi capaz de armazenar chaves e valores não String, apesar de ser contra a intenção. Se a composição tivesse sido usada em vez disso, a API poderia ter funcionado apenas com chaves / valores de string e tudo estaria bem.

Você pode querer algo assim:

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

aparentemente, também não é possível fazer explicitamente Properties<String,String> properties = new Properties<String,String>();. Peculiar.
eis

1
@eis Isso é intencional, em Propertiessi não é genérico.
Mattias Buelens de

Eu prefiro dizer que é por uma série de escolhas infelizes do que pelo design, mas sim.
eis

2
@eis: Não, é por design que Propriedades deve ser um mapa string-a-string. Faz sentido que não seja genérico. Não faz sentido você adicionar chaves / valores que não sejam strings.
Jon Skeet de


8

Se você sabe que seu Propertiesobjeto contém apenas <String, String>entradas, pode recorrer a um tipo bruto:

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

4

O problema é que Propertiesimplementa Map<Object, Object>, enquanto o HashMapconstrutor espera um Map<? extends String, ? extends String>.

Essa resposta explica essa decisão (bastante contra-intuitiva). Resumindo: antes do Java 5, Propertiesimplementado Map(já que não havia genéricos naquela época). Isso significa que você pode colocar qualquer Object um em um Propertiesobjeto. Isso ainda está na documentação:

Como Propertiesherda de Hashtable, os métodos pute putAllpodem ser aplicados a um Propertiesobjeto. Seu uso é fortemente desencorajado, pois permitem que o chamador insira entradas cujas chaves ou valores não sejam Strings. O setPropertymétodo deve ser usado em seu lugar.

Para manter a compatibilidade com isso, os designers não tiveram outra escolha a não ser fazer com que ele herdasse Map<Object, Object>em Java 5. É um resultado infeliz do esforço para obter compatibilidade total com versões anteriores, o que torna o novo código desnecessariamente complicado.

Se você sempre usa propriedades de string em seu Propertiesobjeto, deverá conseguir fazer uma conversão não verificada em seu construtor:

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

ou sem quaisquer cópias:

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

Esta é a assinatura do construtor HashMap public HashMap(Map<? extends K, ? extends V> m). Ele não espera umMap<String, String>
Mubin

@Mubin Ok, simplifiquei um pouco o assunto. Ainda assim, o argumento é válido: a Map<Object, Object>não pode ser usado para um argumento formal do tipo `Map <? extends String,? extends String> `.
Mattias Buelens

2

isso ocorre apenas porque o construtor de HashMap requer um argumento do tipo genérico Map e Properties implementa Map.

Isso funcionará, embora com um aviso

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

1

Você pode usar isto:

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

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

0

Primeira coisa,

A classe Properties é baseada em Hashtable e não em Hashmap. A classe Properties basicamente estende Hashtable

Não existe tal construtor na classe HashMap que pega um objeto de propriedades e retorna um objeto hashmap. Portanto, o que você está fazendo NÃO é correto. Você deve ser capaz de converter o objeto de propriedades para referência de hashtable.


0

Eu uso isso:

for (Map.Entry<Object, Object> entry:properties.entrySet()) {
    map.put((String) entry.getKey(), (String) entry.getValue());
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.