Um mapa é uma estrutura de dados para armazenar pares de chave-valor. É semelhante aos dicionários de outras linguagens de programação. A interface Map define as operações que podes realizar com um mapa.
O Java tem várias implementações distintas de mapas. O HashMap é uma das mais utilizadas.
// Make an instance
Map<String, Integer> fruitPrices = new HashMap<>();
Quando defines uma variável do tipo Map, recomenda-se que declares a variável como sendo do tipo Map em vez do tipo específico, como no exemplo acima.
Esta prática facilita a alteração da implementação de Map mais tarde.
O HashMap também tem um construtor de cópia.
// Make a copy of a map
Map<String, Integer> copy = new HashMap<>(fruitPrices);
Adiciona entradas ao mapa com o método put.
fruitPrices.put("apple", 100);
fruitPrices.put("pear", 80);
// => { "apple" => 100, "pear" => 80 }
Só é possível associar um valor a cada chave.
Se chamares put com a mesma chave, o valor dessa chave é atualizado.
fruitPrices.put("pear", 40);
// => { "apple" => 100, "pear" => 40 }
Usa o método get para obteres o valor de uma chave.
fruitPrices.get("apple"); // => 100
Usa o método containsKey para verificares se o mapa contém uma determinada chave.
fruitPrices.containsKey("apple"); // => true
fruitPrices.containsKey("orange"); // => false
Remove entradas com o método remove.
fruitPrices.put("plum", 90); // Add plum to map
fruitPrices.remove("plum"); // Removes plum from map
O método size devolve o número de entradas.
fruitPrices.size(); // Returns 2
Podes usar os métodos keySet ou values para obteres as chaves ou os valores de um mapa sob a forma de um conjunto (Set) ou de uma coleção, respetivamente.
fruitPrices.keySet(); // Returns "apple" and "pear" in a set
fruitPrices.values(); // Returns 100 and 80, in a Collection
hashCode e equals
O HashMap usa os métodos hashCode e equals do objeto para determinar onde armazenar os valores de uma chave e como os obter. Por este motivo, é importante que os valores devolvidos por esses métodos não mudem entre o momento de armazenar e o de obter, caso contrário o HashMap pode não conseguir encontrar o valor.
Por exemplo, digamos que temos a seguinte classe que será usada como chave de um mapa:
public class Stock {
private String name;
public void setName(String name) {
this.name = name;
}
@Override
public int hashCode() {
return Objects.hash(name);
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (Objects.equals(Stock.class, obj.getClass()) && obj instanceof Stock other) {
return Objects.equals(name, other.name);
}
return false;
}
}
O hashCode e o equals dependem do campo name, que pode ser alterado através de setName.
Alterar o hashCode pode produzir resultados surpreendentes:
Stock stock = new Stock();
stock.setName("Beanies");
Map<Stock, Integer> stockCount = new HashMap<>();
stockCount.put(stock, 80);
stockCount.get(stock); // Returns 80
Stock other = new Stock();
other.setName("Beanies");
stockCount.get(other); // Returns 80 because "other" and "stock" are equal
stock.setName("Choccies");
stockCount.get(stock); // Returns null because hashCode value has changed
stockCount.get(other); // Also returns null because "other" and "stock" are not equal
stock.setName("Beanies");
stockCount.get(stock); // HashCode restored, so returns 80 again
stockCount.get(other); // Also returns 80 again because "other" and "stock" are back to equal
Outra forma comum de criar mapas é usar Map.of ou Map.ofEntries.
// Using Map.of
Map<String, Integer> temperatures = Map.of("Mon", 30, "Tue", 28, "Wed", 32);
// or using Map.ofEntries
Map<String, Integer> temperatures2 = Map.ofEntries(
Map.entry("Mon", 30),
Map.entry("Tue", 28),
Map.entry("Wed", 32)
);
Ao contrário do HashMap, preenchem o mapa de antemão e tornam-se só de leitura depois de criados.
O Map.copyOf cria uma cópia só de leitura de um mapa.
Map<String, Integer> readOnlyFruitPrices = Map.copyOf(fruitPrices);
Chamar métodos como put, remove ou clear provoca uma UnsupportedOperationException.