Reputation: 115
//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;
}
}
Upvotes: 3
Views: 2080
Reputation: 120848
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.
Upvotes: 3
Reputation: 393771
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.
P.S. 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.
Upvotes: 5