简体   繁体   中英

Unable to get value for an object from Hashmap, even though it returns the same hashcode

在此输入图像描述 //Unable to get the value of an object in hashmap after overriding its hashcode //This is the item class where i generate hascode based on the item name

 public class Item {
                    private String name;
                    private Long id;
                    private double price;
                //Constructor
                    public Item(String name, Long id, double price) {
                        this.name = name;
                        this.id = id;
                        this.price = price;
                    }

                    public String getName() {
                        return name;
                    }

                    public void setName(String name) {
                        this.name = name;
                    }

                    public Long getId() {
                        return id;
                    }

                    public void setId(Long id) {
                        this.id = id;
                    }

                    public double getPrice() {
                        return price;
                    }

                    public void setPrice(double price) {
                        this.price = price;
                    }
                //Generating hashcode based on name comparing if item id are //same        
                    @Override
                    public int hashCode() {
                        return name.hashCode();
                    }
                    @Override
                    public boolean equals(Object obj) {
                        return ((Item) obj).id ==(id);
                    }

                    @Override
                    public String toString() {
                        return "Item {" +
                                " name='" + name + '\'' +
                                ", id=" + id +
                                ", price=" + price +
                                '}';
                    }
        //Here there are items but when i pass the exact same item with exact //credentials i get null while checking how many items are there using Hashmap.
                public class Warehouse {
                    Map<Item, Integer> itemList = new HashMap<>();
                    List<Drone> drones = new ArrayList<>();
                    private Drone drone;
                    private String sourceAddress;
                    private Address address;

                    public Warehouse(Address address) {
                        this.address = address;
                        Item item = new Item("PlayStation 4 pro", (long) 100, 42000);
                        Item item1 = new Item("X box one S", (long) 200, 40000);
                        Item item2 = new Item("Apple Macbook Pro", (long) 500, 82000);
                        Item item3 = new Item("Dell Xps Laptop", (long) 1000, 92000);
                        Item item4 = new Item("iPhone 7 plus", (long) 2000, 72000);

                        itemList.put(item, 10);
                        itemList.put(item1, 20);
                        itemList.put(item2, 40);
                        itemList.put(item3, 50);
                        itemList.put(item4, 20);

                    }

                    public Drone getDrone() {
                        return new Drone();
                    }

                    public void setDrone(Drone drone) {
                        this.drone = drone;
                        System.out.println("Drone # " + drone.getDroneID() + " has arrived at the warehouse " + address);
                    }

                    public Address getAddress() {
                        return address;
                    }


                    public ArrayList<Item> getItemList() {
                        return (ArrayList<Item>) itemList;
                    }
                //Setting the item
                    public void setItem(Item item) {
                        Integer num = itemList.get(item);
                        if (num == null) {
                            num = 0;
                        }
                        this.itemList.put(item, ++num);

                    }

//This is where i'm facing the issue, if i query the hashmap for the same item it returns me null, Item even returns the same hashcode

                    public Item removeItem(Item item) {
                        Integer num = itemList.get(item);
                        //## Issue is i get null in num 
                        if(null!= num||num!=0  ){
                            itemList.put(item,num-1);
                        }
                        System.out.println(item);
                        return item;
                    }

            }

Your object's hashCode used the name property, but your equals uses the id property. This violates the contract. Objects for which equals returns true must have the same hashCode .

HashMap uses both hashCode and equals to locate a key. First it locates a bin within the HashMap based on the hashCode . Then it goes over all the entries in the bin using equals to find the key you are looking for. When hashCode doesn't match equals , two objects which you consider to be equal may be mapped to different bins, so using map.contains(key) to find the key stored in the Map will fail.

I think it would make more sense to use id as the criteria for equality, so I'd write:

@Override
public int hashCode() {
    return id.hashCode();
}

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

Note that I used equals for comparing the id s. Comparing objects with == ( Long s in your case) is usually wrong.

Also, you might want to make your equals method more safe by checking the type of obj before casting it to Item , and returning false if the type doesn't match.

PS based on your new code, you have another problem :

This condition :

if(null!= num||num!=0  )

will either be true (if num != null ) or throw a NullPointerException (if num is null ).

Therefore it will only put item in the Map if it's already in the Map . It's not clear what the desired logic is, but it doesn't look right.

The decision of where/which bucket some Entry goes is taken based on your hashcode . But there could be many entries in that bucket.

So equals is called to identify the Entry you are interested in. Since hashcode and equals are un-related (different properties) that introduces inconsistency.

So suppose you have this:

  EntryA (hashCode = 42, id = 2)
  EntryB (hashCode = 44, id = 2)

These entries are equal based on id ; but since they have different hashcodes they will go to different buckets in the HashMap, in different parts.

So now you will have two same entries (according to equals) in the Map - that is why hashcode and equals must be consistent with each other .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM