Médio Java

Chave composta em HashMap

Enunciado

Crie uma classe Chave que representa uma chave composta por dois campos String. Sobrescreva equals e hashCode corretamente. Use essa chave em um HashMap para associar a um valor inteiro. Teste inserir duas chaves iguais e verificar que o valor é sobrescrito.

Requisitos

  • A classe Chave deve ter dois campos String (ex.: parte1 e parte2).
  • equals deve comparar ambos os campos usando Objects.equals.
  • hashCode deve usar Objects.hash com ambos os campos.
  • O programa deve demonstrar que duas chaves iguais acessam o mesmo valor no HashMap.

Código inicial

import java.util.*;

class Chave {
    private String parte1, parte2;

    Chave(String parte1, String parte2) {
        this.parte1 = parte1;
        this.parte2 = parte2;
    }

    // TODO: equals e hashCode
}

public class Main {
    public static void main(String[] args) {
        Map<Chave, Integer> mapa = new HashMap<>();
        Chave k1 = new Chave("a", "b");
        Chave k2 = new Chave("a", "b");

        mapa.put(k1, 10);
        mapa.put(k2, 20);

        System.out.println("Valor para k1: " + mapa.get(k1));
        System.out.println("Tamanho do mapa: " + mapa.size());
    }
}

Saída esperada

Valor para k1: 20
Tamanho do mapa: 1
Ver dica

Lembre-se de que Objects.equals e Objects.hash aceitam múltiplos argumentos.

Mostrar solução
import java.util.*;

class Chave {
    private String parte1, parte2;

    Chave(String parte1, String parte2) {
        this.parte1 = parte1;
        this.parte2 = parte2;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Chave chave = (Chave) o;
        return Objects.equals(parte1, chave.parte1) &&
               Objects.equals(parte2, chave.parte2);
    }

    @Override
    public int hashCode() {
        return Objects.hash(parte1, parte2);
    }
}

public class Main {
    public static void main(String[] args) {
        Map<Chave, Integer> mapa = new HashMap<>();
        Chave k1 = new Chave("a", "b");
        Chave k2 = new Chave("a", "b");

        mapa.put(k1, 10);
        mapa.put(k2, 20);

        System.out.println("Valor para k1: " + mapa.get(k1));
        System.out.println("Tamanho do mapa: " + mapa.size());
    }
}

Passo a passo

  1. A classe Chave armazena duas strings que compõem a chave.
  2. No equals, verificamos identidade, nulidade e classe, depois comparamos cada campo com Objects.equals, que trata null.
  3. No hashCode, usamos Objects.hash(parte1, parte2) para gerar um hash baseado nos mesmos campos.
  4. No main, criamos duas chaves iguais e as inserimos no HashMap.
  5. Como são iguais, a segunda inserção sobrescreve o valor da primeira.
  6. O get com k1 retorna o valor atualizado (20) e o tamanho do mapa é 1.

Por que funciona

O HashMap usa hashCode para localizar o balde e equals para comparar chaves. Como k1 e k2 são iguais e têm o mesmo hash, o mapa as considera a mesma chave, sobrescrevendo o valor. Isso demonstra o contrato na prática.

Erros comuns

  • Não sobrescrever hashCode: o mapa trataria k1 e k2 como chaves diferentes, resultando em tamanho 2 e get(k1) retornando 10.
  • Usar == para comparar strings: parte1 == chave.parte1 compara referências, não conteúdo. Use Objects.equals.
  • Esquecer de tratar null: se parte1 for nulo, parte1.equals(...) lançaria NullPointerException. Objects.equals evita isso.

Outra forma de resolver

Usar Map.Entry ou uma record como chave:

record Chave(String parte1, String parte2) {}

Records geram equals e hashCode automaticamente, sendo mais conciso e menos propenso a erros.

Saída esperada

Valor para k1: 20
Tamanho do mapa: 1