I have an issue with the Hashtable class. I have a class TmpClass implementing a method equals. Then I create a Hashtable, and two objects of TmpClass being equal under my predifined equals method. Then I put of the object as a key in the Hashtable.
But when I test if the second object is actually contained in the Hashtable, the result is "false"...
Here is my main method.
public static void main(String[] args){
Hashtable<TmpClass, Integer> list = new Hashtable<TmpClass, Integer>();
TmpClass v1 = new TmpClass(1);
list.put(v1, 1);
TmpClass v2 = new TmpClass(1);
if(v2.equals(v1))
System.out.println("Equals");
else System.out.println("Not equal");
if(list.containsKey(v2))
System.out.println("Contains");
else System.out.println("Not contain");
}
Here is my TmpClass.
public class TmpClass {
private int val;
public TmpClass(int v){
val = v;
}
public boolean equals(Object o){
if(o instanceof TmpClass){
return val == ((TmpClass) o).val;
}
else return false;
}
}
It's clearly written in the javadoc that the method containsKey of Hashtable uses the method equals of the Object class to compare the keys. Does somebody have a explanation why then the inheritance property is not satisfied here? Or does somebody have an alternative way to solve this problem?
It would be very helpful for me. Thanks.
Implementing equals is not enough.
In order to use your class as a key in a Map you must also implement hashCode.
here is an example:
#Override
public int hashCode() {
return Integer.valueOf(val).hashCode();
}
You must also implement the hashcode method, as part of the hashcode equals contract. It's also stated in the Hastable spec:
To successfully store and retrieve objects from a hashtable, the objects used as keys must implement the hashCode method and the equals method.
Related
I have a program to search the key to print the values from a hashmap. But my inputs to the Key and Values are objects that are user defined.Now when I'm equating input key with key1 why are the hashcodes of the Objects key and key1 in the program appearing different, although the return type is same,ie. NameInit, where the hashcodes of the String str="abc" and abc are returned equal? How to check the equality of key and key1 in the program? I tried Objects.equals(key,key1) after type-casting to Object class, but still did not work.I have seen questions of similar kind like in [this question][1] that discusses about the hashcode equality, but then again how to do the equality of these objects as in my example. Kindly help.
NameInit Class
public class NameInit {
String name;
public NameInit(String name)
{
this.name = name;
}
#Override
public String toString(){
return name;
}
}
PlaceAndAddInit
public class PlaceAndAddInit {
String place;
int value;
public PlaceAndAddInit(String place,int val) {
this.place = place;
this.value= val;
}
#Override
public String toString(){
return place+" "+value;
}
}
Main Class
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
HashMap h = new HashMap();
System.out.println("Limit: ");
int limit = scan.nextInt();
for(int i=0;i<limit;i++)
{
h.put(new NameInit(scan.next()), new PlaceAndAddInit(scan.next(),
scan.nextInt()));
}
System.out.println("Enter a key to search the value: ");//
NameInit key= new NameInit(scan.next());//asks for a input from the user to fetch the values
Set s = h.entrySet();
Iterator<NameInit> itr = s.iterator();
while(itr.hasNext())
{
Map.Entry<NameInit,PlaceAndAddInit> me = (Map.Entry) itr.next();
NameInit key1 =me.getKey();
if(key.equals(key1)){// this never happens with this code as key and key1 holds different hashcodes. So how do I achieve the equality.
System.out.println(me.getValue());
}
}
}
}
Edit: I tried to obtain equality by equals method to which I discovered that hashcodes of key1 and key are different. Understanding the reason behind this is the purpose of my question.
You're not overriding hashCode() so the default is used. In the default implementation, key and key1 will have different hashCode values and they will not be equal even if you think they should be. So the solution is to override the hashCode and equals method if you want to be able to compare those objects.
To answer your question:
Edit: I tried to obtain equality by equals method to which I
discovered that hashcodes of key1 and key are different. Understanding
the reason behind this is the purpose of my question.
If you don't provide overrides to equals and hashCode, they will get inherited from Object. And here's how they look for Object:
public boolean equals(Object obj) {
return (this == obj);
}
Therefore, 2 objects will only be equal when they are == which means that they point to precisely the same memory location. In your example, it is not the case. hashCode is native so can't show you the source code.
Here's more to read:
Google search about hashCode and equals
Objects.equals is implemented like this:
return (a == b) || (a != null && a.equals(b));
You see, it is basically calling a's equals method, not the hashcode method. No matter how hashcode is implemented Objects.equals returns false when a.equals(b) returns false. It has nothing to do with the hashcode method.
So to fix this, simply override the equals method. This is a simple implementation:
#Override
public boolean equals(Object obj) {
return this.hashcode() == obj.hashcode();
}
Also, if you want to find the value of a key in the hash map, call the get method on the hash map and it will do it for you in O(1) time. No need for such an inefficient approach with O(n) time.
I'm following a tutorial to better understand Natural Ordering, using TreeSet and the Comparable interface.
The tutorial tells me that, to add non-primitive custom objects to Sets, I need to implement equals() and hashCode(). However, even without implementing these methods I'm able to compile and run the code (as below). I am using IntelliJ with Java 8.
Is overriding equals() and hashCode() absolutely necessary when working with TreeSets (SortedSet interface) and natural ordering?
class My_Person implements Comparable<My_Person>{
private String name;
public My_Person(String name) {
this.name = name;
}
public String toString() {
return name;
}
// #Override
// public boolean equals(Object o) {
// if (this == o)
// return true;
// if (o == null || getClass() != o.getClass())
// return false;
// My_Person my_person = (My_Person) o;
// return Objects.equals(name, my_person.name);
// }
//
// #Override
// public int hashCode() {
// return Objects.hash(name);
// }
#Override
public int compareTo(My_Person person) {
return name.compareTo(person.name);
}
}
public class NaturalOrdering {
public static void main(String[] args) {
List<My_Person> list = new ArrayList<>();
Set<My_Person> set = new TreeSet<>();
addElement(list);
addElement(set);
Collections.sort(list);
showElement(list);
System.out.println("\n");
showElement(set);
}
private static void addElement(Collection<My_Person> collection) {
collection.add(new My_Person("Joe"));
collection.add(new My_Person("Sue"));
collection.add(new My_Person("Juliet"));
collection.add(new My_Person("Clare"));
collection.add(new My_Person("Mike"));
}
private static void showElement(Collection<My_Person> collection) {
for(My_Person element: collection) {
System.out.println(element);
}
}
}
This depends on your requirements for equality. If you don't override equals and hashCode, two objects are defined as equal if and only if they are identical (i.e. the same object). If you need some other definition for equality you must override the methods.
It is not "absolutely necessary", but then you may get unexpected/wrong output if you don't do so. So if you don't override them, it may still work sometimes, but it may fail too. So just to be safe, better override it.
It will fail if you need to check equality. But if you are only concerned with sorting of the stored objects as per your own defined logic in compareTo method, then I think there is no need to override equals or hashcode.
Its not required by TreeSet or for that matter any Set. But Set is by nature collection of unique objects. For primitive types, Java has way to know whether two objects are same or not. For non-primitive user has to tell Java on how to know whether two objects are same or not and way is to override equals and hashcode methods which are invoked by Set#add method to determine the object being added already exists in the set or not. So if you need a functional unique objects in your Set, you should implement equals and hashcode methods. Hope this helps. This is a good read on the same topic.
You have the source code for TreeSet (and TreeMap which TreeSet uses under the hood). You can clearly see that TreeSet (and TreeMap) rely on compareTo() and not hashCode().
On the other hand, the HashSet (and HashMap which HashSet uses under the hood) does use hashCode() and equals(). No big surprise, considering that they are named HashSet and HashMap.
overriding equals() and hashCode() is not required for TreeMap while required for HashMap and LinkedHashMap.
TreeMap works on implementation provided by Comparable or Comparator interface when using user created Classes as Key for Map
Is this correct?
Internally TreeMap use only compare function for keys.
The method containsValue use internally equals. So it is necessary to redefine equals for values, not for keys.
HashMap and LinkedHashMap use internally equals and hashCode on the keys. So you need to redefine it for your key class. As for TreeMap also HashMap and LinkedHashMap uses equals for the values. So it is necessary to redefine equals for values.
To answer such questions it's much better to read the documentation instead of performing a test. Test shows nothing. Even if you see that methods are not called, it does not mean that they will not be called in further Java versions or using another JDK vendor. Or probably these methods could be called when performing some other operations with your map.
In this particular case the documentation says the following:
Note that the ordering maintained by a tree map, like any sorted map, and whether or not an explicit comparator is provided, must be consistent with equals if this sorted map is to correctly implement the Map interface. (See Comparable or Comparator for a precise definition of consistent with equals.) This is so because the Map interface is defined in terms of the equals operation, but a sorted map performs all key comparisons using its compareTo (or compare) method, so two keys that are deemed equal by this method are, from the standpoint of the sorted map, equal. The behavior of a sorted map is well-defined even if its ordering is inconsistent with equals; it just fails to obey the general contract of the Map interface.
Emphasis mine.
So according to the last sentence the TreeMap will be fully functional if you not define the equals method for the keys, but it will violate the contract of the Map interface. For example, Map.containsKey contract is the following:
Returns true if this map contains a mapping for the specified key. More formally, returns true if and only if this map contains a mapping for a key k such that (key==null ? k==null : key.equals(k)).
So this would be wrong if you use the TreeMap with keys having inconsistent equals implementation. If you pass your map to some method which assumes that the passed map follows the contract, then that method may work incorrectly.
I always follow a simple recipe: for all map implementations, I always override hashCode() and equals(). If the map is also a SortedMap, I also override compare() or implement a Comparator (in both cases the comparison must be consistent with equals(), which means that the result of the comparison must return 0 if the elements being compared are actually equal).
This recipe allows me to use whichever map implementation I feel is the most appropriate for the specific problem I'm tackling, and it even allows me to use different map implementations for the same key class.
Yes, i am correct (Please Correct me if I am wrong)... Check Following Code -
In this following code `equals()` and `hashCode()` is never get called , so in `TreeMap` does these never gets called in `TreeMap` ?? Or something wrong I have Done ???? its never printing `inside hashCode()` `inside equals()`
package Map;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
public class HashCodeEqualsTester {
public static void main (String[] args){
Car car1 = new Car("788");
Driver driver1 = new Driver("Kevin");
Car car2 = new Car("656");
Driver driver2 = new Driver("Bob");
Car car3 = new Car("343");
Driver driver3 = new Driver("Stuart");
Map<Car, Driver> myMap = new TreeMap<Car, Driver>();
// Map<Car, Driver> myMap = new LinkedHashMap<Car, Driver>();
// Map<Car, Driver> myMap = new HashMap<Car, Driver>();
// try to run these 3 one at a time and see how does it behave
myMap.put(car1, driver1);
myMap.put(car2, driver2);
myMap.put(car3, driver3);
System.out.println(myMap);
}
}
class Car implements Comparable{
private String carNumber;
public Car (String carNumber){
this.carNumber = carNumber;
}
public String getCarNumber() {
return carNumber;
}
public void setCarNumber(String carNumber) {
this.carNumber = carNumber;
}
public String toString(){
return ("Car Number : " + carNumber);
}
public int hashCode(){
System.out.println("Inside hashCode()");
int hashCode = 1;
hashCode = hashCode * this.getCarNumber().hashCode();
System.out.println("For Car Number : " + this.getCarNumber() + " hashcode is : " + hashCode);
//return hashCode;
return 1000;
}
public boolean equals(Object o){
System.out.println("Inside equals()");
if (!(o instanceof Car)) {
return false;
} else {
Car car = (Car) o;
return (this.getCarNumber().equalsIgnoreCase(car.getCarNumber()));
}
}
public int compareTo(Object o) {
Car car = (Car) o;
return (this.getCarNumber().compareTo(car.getCarNumber()));
}
}
class Driver {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String toString(){
return (this.getName() + " ");
}
public Driver(String name){
this.name = name;
}
}
I've implemented the Apriori algorithm. it works pretty well, but I ran into a strange problem: I've defined a Rule class to maintain the generated rules.
Here it is:
public class Rule
{
private Set<Integer> left;
private Set<Integer> right;
private LookupArtist lookupArtist;
public Rule(LookupArtist lookupArtist){
left = new HashSet<>();
right = new HashSet<>();
this.lookupArtist = lookupArtist;
}
#Override
public boolean equals(Object another){
Rule rule = (Rule) another;
if(this.left.equals(rule.getLeft()) && this.right.equals(rule.getRight()))
return true;
else
return false;
}
#Override
public String toString(){
/* print the object */
}
public void addToLeft(Integer toAdd){
left.add(toAdd);
}
public void addToRight(Integer toAdd){
right.add(toAdd);
}
public Set<Integer> getLeft(){
return left;
}
public Set<Integer> getRight(){
return right;
}
}
I also implemented the equals() method in a different way just to try:
#Override
public boolean equals(Object another){
Rule rule = (Rule) another;
boolean flag = true;
for(Integer artist : left){
if(flag)
if(!rule.left.contains(artist))
flag=false;
}
if(flag)
for(Integer artist : right){
if(flag)
if(!rule.right.contains(artist))
flag=false;
}
return flag;
}
The LookupArtist object is used to map the integers to some Strings.
The problem is that when I print out the rules I found that some rules appear two times. I also found in debug mode some replicated rules, so it isn't be a print problem. The rules are saved in a map like this:
static Map<Rule, Float> rules;
.
.
.
Rule rule = new Rule(lookupArtist);
for(int j=0;j<i;j++){
rule.addToLeft(a[j]);
}
for(int j=i;j<a.length;j++){
rule.addToRight(a[j]);
}
if(!rules.containsKey(rule)){
rules.put(rule, getRuleConfidence(rule));
}
Any idea where the problem can be?
When using a HashSet for storing objects of a class that has a custom equals implementation, you must have a matching custom implementation for hashCode.
If two objects are equal (according to the custom equals implementation), they must have the samehashCode. In the code you posted, I don't see an overriding ofhashCodein theRule` class.
When you add an instance to the HashSet, hashCode method is used to determine the index in the hash table in which the instance will be stored. Then, the linked list of instances stored in this index is iterated to see if the instance is already there. When iterating over that list, equals is used. If two objects that are equal are mapped by hashCode to different indices in the HashSet, the duplication won't be detected, since they would be stored in separate linked lists.
This is stated in the Javadoc of equals :
* Note that it is generally necessary to override the <tt>hashCode</tt>
* method whenever this method is overridden, so as to maintain the
* general contract for the <tt>hashCode</tt> method, which states
* that equal objects must have equal hash codes.
And in the Javadoc of hashCode :
* <li>If two objects are equal according to the <tt>equals(Object)</tt>
* method, then calling the <code>hashCode</code> method on each of
* the two objects must produce the same integer result.
You should always override hashCode when you override equals and vice versa.
Add something like this to your Rule class:
#Override
public int hashCode() {
return left.hashCode()
^ right.hashCode()
^ lookupArtist.hashCode();
}
Here is a good answer explaining why it's important to override both.
Also, your equals method can be written as
#Override
public boolean equals(Object another){
Rule rule = (Rule) another;
return left.equals(rule.left)
&& right.equals(rule.right)
&& lookupArtist.equals(rule.lookupArtist);
}
A final remark: Your other attempt at the equals-implementation is not symmetrical, i.e. it's not the case that rule1.equals(rule2) if and only if rule2.equals(rule1). That's a violation of the contract of equals.
And where is your hashCode() method? It is also very important :)
I just did the following code:
import java.util.HashSet;
import java.util.Set;
public class MyClass {
private static class MyObject {
private int field;
public int getField() {
return field;
}
public void setField(int aField) {
field = aField;
}
#Override
public boolean equals(Object other) {
boolean result = false;
if (other != null && other instanceof MyObject) {
MyObject that = (MyObject) other;
result = (this.getField() == that.getField());
}
return result;
}
#Override
public int hashCode() {
return field;
}
}
public static void main(String[] args) {
Set<MyObject> mySet = new HashSet<MyObject>();
MyObject object = new MyObject();
object.setField(3);
mySet.add(object);
object.setField(5);
System.out.println(mySet.contains(object));
MyObject firstElement = mySet.iterator().next();
System.out.println("The set object: " + firstElement + " the object itself: " + object);
}
}
It prints:
false
The set object: MyClass$MyObject#5 the object itself: MyClass$MyObject#5
Basically meaning that the object is not considered to be in the set, whiile its instance itself apparantly is in the set. this means that if I insert a object in a set, then change the value of a field that participates in the calculation of the hashCode method, then the HashSet method will seize working as expected. Isn;t this too big source of possible errors? How can someone defend against such cases?
Below is the quote from Set API. It explains everything.
Note: Great care must be exercised if mutable objects are used as set elements. The behavior of a set is not specified if the value of an object is changed in a manner that affects equals comparisons while the object is an element in the set. A special case of this prohibition is that it is not permissible for a set to contain itself as an element.
http://docs.oracle.com/javase/7/docs/api/java/util/Set.html
HashSet is implemented on HashMap.
HashMap caches the hashCode of the key, So if you change the hashCode than even though the hash function maps the hashCode to the same bucket as the original object present but it will not find because before even checking the object equality it will check the hashCode.
see the line:
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
And if the hashCode maps to different bucket by hash function than the original object is present than obviously it can't find.
So even though same object if you change the hashCode hashSet can't find. Hope it helps.
So key for the HashMap or the object that you are putting into HashSet should be immutable or effective immutable.
#fazomisiek
public HashSet() {
map = new HashMap<E,Object>();
}
Similarly if you check the source of HashSet you can find it.
This problem is just a limitation of the implementation of java.util.HashSet and the underlying java.util.HashMap. Fundamentally, you're trading off the ability to modify elements in the set for faster insert/lookup performance - it's just part of the contract of using a hash set / map data structure.
If you can't guarantee everybody will remember they can't modify the objects in the set, the only way to absolutely guard against this happening is to only insert immutable objects into the set in the first place.