O Guava nos fornece ótimos métodos de fábrica para tipos Java, como Maps.newHashMap()
.
Mas também existem construtores para mapas java?
HashMap<String,Integer> m = Maps.BuildHashMap.
put("a",1).
put("b",2).
build();
O Guava nos fornece ótimos métodos de fábrica para tipos Java, como Maps.newHashMap()
.
Mas também existem construtores para mapas java?
HashMap<String,Integer> m = Maps.BuildHashMap.
put("a",1).
put("b",2).
build();
Respostas:
Uma vez que a Map
interface Java 9 contém:
Map.of(k1,v1, k2,v2, ..)
Map.ofEntries(Map.entry(k1,v1), Map.entry(k2,v2), ..)
. As limitações desses métodos de fábrica são:
null
s como chaves e / ou valores (se você precisar armazenar nulos, dê uma olhada em outras respostas)Se precisarmos de um mapa mutável (como HashMap), podemos usar seu construtor de cópia e deixá-lo copiar o conteúdo do mapa criado viaMap.of(..)
Map<Integer, String> map = new HashMap<>( Map.of(1,"a", 2,"b", 3,"c") );
null
valores, o que pode ser um problema dependendo do caso de uso.
Map.of(k1,v1, k2,v2, ...)
pode ser usado com segurança quando não temos muitos valores. Para uma quantidade maior de valores Map.ofEntries(Map.entry(k1,v1), Map.entry(k2,v2), ...)
nos dá um código mais legível que é menos sujeito a erros (a menos que eu tenha entendido mal).
Não existe tal coisa para HashMaps, mas você pode criar um ImmutableMap com um construtor:
final Map<String, Integer> m = ImmutableMap.<String, Integer>builder().
put("a", 1).
put("b", 2).
build();
E se você precisar de um mapa mutável, basta alimentá-lo para o construtor HashMap.
final Map<String, Integer> m = Maps.newHashMap(
ImmutableMap.<String, Integer>builder().
put("a", 1).
put("b", 2).
build());
ImmutableMap
não suporta null
valores. Portanto, há uma limitação dessa abordagem: você não pode definir valores em seu HashMap
para null
.
new HashMap
construtor Java em vez do Maps.newHashMap
método estático ?
Não é bem um construtor, mas usa um inicializador:
Map<String, String> map = new HashMap<String, String>() {{
put("a", "1");
put("b", "2");
}};
map instanceof HashMap
falso? Parece uma ideia não muito boa.
map.getClass()==HashMap.class
retornará falso. Mas isso é um teste estúpido de qualquer maneira. HashMap.class.isInstance(map)
deve ser preferido, e isso retornará verdadeiro.
Isso é semelhante à resposta aceita, mas um pouco mais clara, a meu ver:
ImmutableMap.of("key1", val1, "key2", val2, "key3", val3);
Existem várias variações do método acima e são ótimas para fazer mapas estáticos, imutáveis e imutáveis.
Aqui está um muito simples ...
public class FluentHashMap<K, V> extends java.util.HashMap<K, V> {
public FluentHashMap<K, V> with(K key, V value) {
put(key, value);
return this;
}
public static <K, V> FluentHashMap<K, V> map(K key, V value) {
return new FluentHashMap<K, V>().with(key, value);
}
}
então
import static FluentHashMap.map;
HashMap<String, Integer> m = map("a", 1).with("b", 2);
Consulte https://gist.github.com/culmat/a3bcc646fa4401641ac6eb01f3719065
Um construtor de mapa simples é fácil de escrever:
public class Maps {
public static <Q,W> MapWrapper<Q,W> map(Q q, W w) {
return new MapWrapper<Q, W>(q, w);
}
public static final class MapWrapper<Q,W> {
private final HashMap<Q,W> map;
public MapWrapper(Q q, W w) {
map = new HashMap<Q, W>();
map.put(q, w);
}
public MapWrapper<Q,W> map(Q q, W w) {
map.put(q, w);
return this;
}
public Map<Q,W> getMap() {
return map;
}
}
public static void main(String[] args) {
Map<String, Integer> map = Maps.map("one", 1).map("two", 2).map("three", 3).getMap();
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println(entry.getKey() + " = " + entry.getValue());
}
}
}
Você pode usar:
HashMap<String,Integer> m = Maps.newHashMap(
ImmutableMap.of("a",1,"b",2)
);
Não é tão elegante e legível, mas faz o trabalho.
Map<String, Integer> map = ImmutableMap.of("a", 1, "b", 2);
, Melhor?
HashMap
é mutável; não há necessidade de um construtor.
Map<String, Integer> map = Maps.newHashMap();
map.put("a", 1);
map.put("b", 2);
ImmutableSet
. Se você realmente quiser que ele seja mutável, pode inicializá-lo no construtor ou em um bloco inicializador de instância ou em um bloco inicializador estático, se for um campo estático.
ImmutableMap
lá obviamente.
{{init();}}
não estática (não no construtor, já que outro construtor pode esquecê-la). E é bom que seja uma espécie de ação atômica. Se o mapa for volátil, inicializá-lo com um construtor garante que ele esteja sempre null
no estado final, nunca preenchido pela metade.
Você pode usar a API fluente nas coleções do Eclipse :
Map<String, Integer> map = Maps.mutable.<String, Integer>empty()
.withKeyValue("a", 1)
.withKeyValue("b", 2);
Assert.assertEquals(Maps.mutable.with("a", 1, "b", 2), map);
Aqui está um blog com mais detalhes e exemplos.
Nota: Eu sou um committer para Eclipse Collections.
Eu tinha um requisito semelhante há um tempo. Não tem nada a ver com goiaba, mas você pode fazer algo assim para ser capaz de construir umMap
usando um construtor fluente.
Crie uma classe base que estenda Map.
public class FluentHashMap<K, V> extends LinkedHashMap<K, V> {
private static final long serialVersionUID = 4857340227048063855L;
public FluentHashMap() {}
public FluentHashMap<K, V> delete(Object key) {
this.remove(key);
return this;
}
}
Em seguida, crie o construtor fluente com métodos que atendam às suas necessidades:
public class ValueMap extends FluentHashMap<String, Object> {
private static final long serialVersionUID = 1L;
public ValueMap() {}
public ValueMap withValue(String key, String val) {
super.put(key, val);
return this;
}
... Add withXYZ to suit...
}
Você pode então implementá-lo assim:
ValueMap map = new ValueMap()
.withValue("key 1", "value 1")
.withValue("key 2", "value 2")
.withValue("key 3", "value 3")
Isso é algo que eu sempre quis, especialmente durante a configuração de acessórios de teste. Finalmente, decidi escrever um construtor fluente simples que pudesse construir qualquer implementação de mapa - https://gist.github.com/samshu/b471f5a2925fa9d9b718795d8bbdfe42#file-mapbuilder-java
/**
* @param mapClass Any {@link Map} implementation type. e.g., HashMap.class
*/
public static <K, V> MapBuilder<K, V> builder(@SuppressWarnings("rawtypes") Class<? extends Map> mapClass)
throws InstantiationException,
IllegalAccessException {
return new MapBuilder<K, V>(mapClass);
}
public MapBuilder<K, V> put(K key, V value) {
map.put(key, value);
return this;
}
public Map<K, V> build() {
return map;
}
Aqui está um que escrevi
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
public class MapBuilder<K, V> {
private final Map<K, V> map;
/**
* Create a HashMap builder
*/
public MapBuilder() {
map = new HashMap<>();
}
/**
* Create a HashMap builder
* @param initialCapacity
*/
public MapBuilder(int initialCapacity) {
map = new HashMap<>(initialCapacity);
}
/**
* Create a Map builder
* @param mapFactory
*/
public MapBuilder(Supplier<Map<K, V>> mapFactory) {
map = mapFactory.get();
}
public MapBuilder<K, V> put(K key, V value) {
map.put(key, value);
return this;
}
public Map<K, V> build() {
return map;
}
/**
* Returns an unmodifiable Map. Strictly speaking, the Map is not immutable because any code with a reference to
* the builder could mutate it.
*
* @return
*/
public Map<K, V> buildUnmodifiable() {
return Collections.unmodifiableMap(map);
}
}
Você o usa assim:
Map<String, Object> map = new MapBuilder<String, Object>(LinkedHashMap::new)
.put("event_type", newEvent.getType())
.put("app_package_name", newEvent.getPackageName())
.put("activity", newEvent.getActivity())
.build();
Usando java 8:
Esta é uma abordagem do Java-9 Map.ofEntries(Map.entry(k1,v1), Map.entry(k2,v2), ...)
public class MapUtil {
import static java.util.stream.Collectors.toMap;
import java.util.AbstractMap.SimpleEntry;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Stream;
private MapUtil() {}
@SafeVarargs
public static Map<String, Object> ofEntries(SimpleEntry<String, Object>... values) {
return Stream.of(values).collect(toMap(Entry::getKey, Entry::getValue));
}
public static SimpleEntry<String, Object> entry(String key, Object value) {
return new SimpleEntry<String, Object>(key, value);
}
}
Como usar:
import static your.package.name.MapUtil.*;
import java.util.Map;
Map<String, Object> map = ofEntries(
entry("id", 1),
entry("description", "xyz"),
entry("value", 1.05),
entry("enable", true)
);
Underscore-java pode construir hashmap.
Map<String, Object> value = U.objectBuilder()
.add("firstName", "John")
.add("lastName", "Smith")
.add("age", 25)
.add("address", U.arrayBuilder()
.add(U.objectBuilder()
.add("streetAddress", "21 2nd Street")
.add("city", "New York")
.add("state", "NY")
.add("postalCode", "10021")))
.add("phoneNumber", U.arrayBuilder()
.add(U.objectBuilder()
.add("type", "home")
.add("number", "212 555-1234"))
.add(U.objectBuilder()
.add("type", "fax")
.add("number", "646 555-4567")))
.build();
// {firstName=John, lastName=Smith, age=25, address=[{streetAddress=21 2nd Street,
// city=New York, state=NY, postalCode=10021}], phoneNumber=[{type=home, number=212 555-1234},
// {type=fax, number=646 555-4567}]}