user1734698
user1734698

Reputation: 167

HashMap not retrieving both values even after overriding hashcode & equals for same object

I know this question will be pretty amateur but, I having trouble understanding why my hashmap will not store or retrieve values when I use the same object instance as a key. My code is as follows

public class Candidate {

    private  String id;
    private  String name;

    public Candidate (String id, String name){
        this.id = id;
        this.name = name;
    }

    public static void main(String args[]){
        Candidate cad = new Candidate("101","hari");

        HashMap<Candidate,String> mp = new HashMap<Candidate,String>();
        mp.put(cad, "sachin");
        mp.put(cad, "shewag");

        for(Candidate cand : mp.keySet()){
            System.out.println(mp.get(cand).toString());
        }
    }

I am overriding hashcode and equals as follows.

@Override 
    public boolean equals(Object obj){
        Candidate cad =(Candidate)obj;
        if(!(obj instanceof Candidate)){
            return false;
        }
        if(cad.id.equals(this.id) && cad.name.equals(this.name)){
            return true;
        }
        return false;
    }

    @Override 
    public int hashCode(){
        return Objects.hash(id, name);
    }

When I try to get the size of the hashmap, it returns as only one. meaning the first insertion into the hashmap was overridden by the second one.

Is it because I am using the same instance of Candidate to insert two values? Is it possible to force hashmap to insert both key,value pairs?

Upvotes: 1

Views: 57

Answers (3)

Eugene
Eugene

Reputation: 120848

There is a simpler way to do what you want with java-8 btw, simplified example:

    HashMap<String, List<String>> mp = new HashMap<>();

    List<String> list = Arrays.asList("aa", "aa", "bb", "bb");

    for (String s : list) {
        mp.computeIfAbsent(s, k -> new ArrayList<>()).add("c");
    }

    System.out.println(mp); // {bb=[c, c], aa=[c, c]}

Upvotes: 3

Hovercraft Full Of Eels
Hovercraft Full Of Eels

Reputation: 285403

The whole idea behind a Map is that 1) keys are unique -- it holds only one key/value pair for a particular key, and 2) its look up is relatively "cheap".

You've only got one object within your HashMap. Understand that when you add another key, value pair to the map, if the key is the same as a previous item in the map, the previous item is replaced by the new one. If you want to add two or more items, then use different keys, or create a Map that holds a List<...> of objects as its value. e.g.,

HashMap<Candidate, List<String>>

In this situation, you would first check to see if the Map holds a Candidate item, and if so, add a new String to its list. If not, then add the new Candidate with a new ArrayList<String> value. Usually I use a method for just this purpose, something like:

public static void put(Candidate cand, String text) {
    if (newMap.containsKey(cand)) {
        newMap.get(cand).add(text);
    } else {
        List<String> list = new ArrayList<>();
        list.add(text);
        newMap.put(cand, list);
    }
}

And yes, as d.j.brown states in comment, fix your equals method to avoid a class cast exception.

Something like so:

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

public class MyCandidateTest {
    private static Map<Candidate, List<String>> newMap = new HashMap<>();

    public static void main(String args[]) {
        Candidate cad = new Candidate("101", "hari");

        put(cad, "Foo");
        put(cad, "Bar");

        for (Candidate cand : newMap.keySet()) {
            System.out.println(newMap.get(cand).toString());
        }
    }

    public static void put(Candidate cand, String text) {
        if (newMap.containsKey(cand)) {
            newMap.get(cand).add(text);
        } else {
            List<String> list = new ArrayList<>();
            list.add(text);
            newMap.put(cand, list);
        }
    }

}

public class Candidate {

    private String id;
    private String name;

    public Candidate(String id, String name) {
        this.id = id;
        this.name = name;
    }

    @Override
    public boolean equals(Object obj) {
        // Candidate cad =(Candidate)obj; // !! no
        if (!(obj instanceof Candidate)) {
            return false;
        }
        Candidate cad = (Candidate) obj; // !! yes
        if (cad.id.equals(this.id) && cad.name.equals(this.name)) {
            return true;
        }
        return false;
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name);
    }
}

Upvotes: 3

Timmos
Timmos

Reputation: 3324

Either use

Upvotes: 0

Related Questions