Reputation: 191
I have a class
public class Customer {
private int customerId;
private String customerName;
private String customerType;
private String customerAddress;
public Customer(int customerId, String customerName, String customerType, String customerAddress) {
super();
this.customerId = customerId;
this.customerName = customerName;
this.customerType = customerType;
this.customerAddress = customerAddress;
}
public int getCustomerId() {
return customerId;
}
public void setCustomerId(int customerId) {
this.customerId = customerId;
}
public String getCustomerName() {
return customerName;
}
public void setCustomerName(String customerName) {
this.customerName = customerName;
}
public String getCustomerType() {
return customerType;
}
public void setCustomerType(String customerType) {
this.customerType = customerType;
}
public String getCustomerAddress() {
return customerAddress;
}
public void setCustomerAddress(String customerAddress) {
this.customerAddress = customerAddress;
}
@Override
public String toString() {
return "Customer [customerId=" + customerId + ", customerName=" + customerName + ", customerType="
+ customerType + ", customerAddress=" + customerAddress + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((customerAddress == null) ? 0 : customerAddress.hashCode());
result = prime * result + ((customerName == null) ? 0 : customerName.hashCode());
result = prime * result + ((customerType == null) ? 0 : customerType.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Customer other = (Customer) obj;
if (customerAddress == null) {
if (other.customerAddress != null)
return false;
} else if (!customerAddress.equals(other.customerAddress))
return false;
if (customerName == null) {
if (other.customerName != null)
return false;
} else if (!customerName.equals(other.customerName))
return false;
if (customerType == null) {
if (other.customerType != null)
return false;
} else if (!customerType.equals(other.customerType))
return false;
return true;
}
}
Notice that I have removed the customerId from equal and hashcode calculation. I Created this method to using customer object as a key
public static Map<Customer, String> testKeysWithObject(){
Map<Customer, String> map = new HashMap<>();
Customer customer1 = new Customer(1, "customerName1", "customerType1", "customerAddress1");
Customer customer2 = new Customer(2, "customerName2", "customerType2", "customerAddress2");
Customer customer3 = new Customer(3, "customerName3", "customerType3", "customerAddress3");
Customer customer4 = new Customer(4, "customerName4", "customerType4", "customerAddress4");
map.put(customer1, "customer1");
map.put(customer2, "customer2");
map.put(customer3, "customer3");
map.put(customer4, "customer4");
customer4 = new Customer(5, "customerName5", "customerType5", "customerAddress5");
customer3.setCustomerAddress("customerAddress5");
System.out.println(customer4.getCustomerAddress());
return map;
}
And the below method to traverse the Hashmap.
public static void displayMap(Map<Customer, String> map) {
System.out.println("================================== ENTRY SET ==========================================");
for (Entry<Customer, String> mapKeys : map.entrySet()) {
if(null != mapKeys)
System.out.println("Key -> " + mapKeys.getKey() + " Value -> " + mapKeys.getValue()+ " HashCode -> " + mapKeys.hashCode());
}
System.out.println();
System.out.println("================================== KEY SET ==========================================");
for (Customer mapKeys : map.keySet()) {
if(null != map.get(mapKeys))
System.out.println("Key -> " + mapKeys + " Value -> " + map.get(mapKeys) + " HashCode -> " + map.get(mapKeys).hashCode());
}
}
and below is the output.
customerAddress5
================================== ENTRY SET ========================================== Key -> Customer [customerId=3, customerName=customerName3,
customerType=customerType3, customerAddress=customerAddress5] Value -> customer3 HashCode -> 291012570 Key -> Customer [customerId=4, customerName=customerName4, customerType=customerType4, customerAddress=customerAddress4] Value -> customer4 HashCode -> 291011640 Key -> Customer [customerId=2, customerName=customerName2, customerType=customerType2, customerAddress=customerAddress2] Value -> customer2 HashCode -> 291210360 Key -> Customer [customerId=1, customerName=customerName1, customerType=customerType1, customerAddress=customerAddress1] Value -> customer1 HashCode -> 291211416================================== KEY SET ========================================== Key -> Customer [customerId=4, customerName=customerName4,
customerType=customerType4, customerAddress=customerAddress4] Value -> customer4 HashCode -> 1611562006 Key -> Customer [customerId=2, customerName=customerName2, customerType=customerType2, customerAddress=customerAddress2] Value -> customer2 HashCode -> 1611562004 Key -> Customer [customerId=1, customerName=customerName1, customerType=customerType1, customerAddress=customerAddress1] Value -> customer1 HashCode -> 1611562003
I have a couple of question on this hashmap behavior
Upvotes: 1
Views: 79
Reputation: 33701
why is hashmap not affected by customer4=new assignment, how does hashcode stores these.
You are assigning new object to customer4
variable, you do not change the object itself. Map holds reference to the old object and does not know that you have changed customer4
.
How is hashmap effected by customer3.setCustomerAddress("customerAddress5");
You are changing the object itself. Both customer3
and customer in the map are pointing to the same object.
Why there are two different values returned by keyset() and entryset methods.
Never put mutable objects as keys. Or at least do not change them after putting into map. The map is unable to handle this change and unable to reorder entries. That is why the key with "customerAddres5"
is "missed".
As I can see changes are reflected in entryset() and not in keyset() that makes me wonder how is hashmap able to cop up with the change with one method and not with another.
entrySet
method returns the whole set with (key -> value) pairs. Your check for null != mapKeys
is redundant here as they are all not null and they are already connected with each other.
keySet
method returns keys only. It also returns 4 items, but you are filtering out entry with "customerAddress5"
because you are trying to get value by this key. However, the hashcode has changed for this key since you changed address
field and the map is unable to retrieve value for this key.
Conclusion: never change key state. Make it immutable so nobody can change it.
Does hashmap store reference for actual objects, if references then why customer4 = new had no impact on hashmap?
You are reassigning customer4
. Read answer for the first question again.
Upvotes: 3
Reputation: 180
Here you are pointing customer4 to a different object.So, HashMap won't get affected. Answer to 4th question is same.
All these are because your Customer object is Mutable. you can refer this and this to know why HashMap keys should be Immutable.
This is because you have changed the Customer object by customer3.setCustomerAddress. When you retrieve Keyset() all the keys will be returned. But when you try to retrieve the value using that key then it will now point to a different bucket(Remember you have used customer address to calculate hashcode).
Upvotes: 0
Reputation: 1972
What you have to understand is that each of your four Customer
variables is simply a reference to an Object somewhere. When you call map.put(customer4, "customer4");
, you are saying 'use the object referenced by the variable customer4
as the key for a value "customer4"'. When you reassign customer4
with the new
statement, the Object is not modified, but rather customer4
is changed so that it no longer references that first Object.
As to your second question, customer3
is likewise a reference to a variable. After you assign customer3
to reference a new
Object, you put that Object in the HashMap
. Now there are two references to this Object; one of them is customer3
, and the other is the entry in the HashMap
. When you call setCustomerAddress()
, you are now operating on that Object, and so the change will be visible from both of those reference points.
As to why the keyset() and entryset() methods return differnet hashCode
s, in your first loop, mapKeys
is of type Entry
, whereas in the second loop, map.get(mapKeys)
is of type String
, so they will naturally produce different hashCode
s.
Finally, everything in Java is a reference. When you put
customer4 into the Map, you were really putting a reference to an Object. When you reassigned customer4, you were simply pointing a variable towards another Object, but the other copy of the reference did not go away. Therefore, changing the variable customer4
did not affect the entry in the Map.
Upvotes: 0