Can someone tell me why this returns true ?
I thought if I cast something to e.g. Object and then call .equals,
the default implementation from Object will be used. And s1 == s2 should return false.
Please tell me under which topic I can find more about this behavior.
Set<String> s1 = new HashSet<String>(as("a"));
Set<String> s2 = new HashSet<String>(as("a"));
Object o1 = (Object)s1;
Object o2 = (Object)s2;
System.out.println(o1.equals(o2));
Because that's exactly what the Javadocs say it will do:
public boolean equals(Object o)
Compares the specified object with this set for equality. Returns true if the given object is also a set, the two sets have the same size, and every member of the given set is contained in this set. This ensures that the equals method works properly across different implementations of the Set interface.
Just because you cast it to Object doesn't change what it actually is. The over-ridden equals() method in HashSet is used.
Methods in Java are virtual by default. In particular, Object.equals is virtual (since it's not declared final). Since HashSet overrides Object.equals1, you'll see the HashSet implementation of equals used when you invoke the virtual method on an object that has a runtime type of HashSet (remember dynamic dispatch depends on the runtime type of the receiving object, not the compile-time type).
1: We know that HashSet overrides Object.equals because the documentation says that HashSet derives from AbstractSet and the documentation for AbstractSet.equals says:
Compares the specified object with this set for equality. Returns true if the specified object is also a set, the two sets have the same size, and every member of the specified set is contained in this set (or equivalently, every member of this set is contained in the specified set). This definition ensures that the equals method works properly across different implementations of the set interface.
which clearly defines value equality whereas the default Object.equals is identity equality.
Because, of Polymorphism.
At runtime, the actual type of object determines which implementation of an instance method should execute. Since, HashSet inherits an overridden implementation from AbstractSet it's used for comparison instead of Object.equals()
And, the JavaDocs for AbstractSet#equals() state
Returns true if the given object is also a set, the two sets have the same size, and every member of the given set is contained in this set.
Hence, your equality test prints true.
So, the type of reference (Object, in your case) has little bearing on which version of equals() is invoked at runtime. It entirely depends on the type of object your reference is pointing to.
Yes the default implementation of equals should return you false. But here default implementation is not called. Object class is extended by everytype in java and that includes HashSet.
Now here if you see, you are referring HashSet object with Object class reference. And hence HashSet equals is called. And hence it returns true.
Related
I try to create a tuple class that allows a tuple-like structure in Java. The general type for two elements in tuple are X and Y respectively. I try to override a correct equals for this class.
Thing is, I know Object.equals falls into default that it still compares based on references like "==", so I am not so sure I can use that. I looked into Objects and there is an equals() in it. Does this one still compare on references, or it compares on contents?
Quickly imagined the return statement as something like:
return Objects.equals(compared.prev, this.prev) && Objects.equals(compared.next, this.next);
where prev and next are elements of tuple. Would this work?
The difference is the Objects.equals() considers two nulls to be "equal". The pseudo code is:
if both parameters are null or the same object, return true
if the first parameter is null return false
return the result of passing the second parameter to the equals() method of the first parameter
This means it is "null safe" (non null safe implementation of the first parameter’s equals() method notwithstanding).
this is literal code from java source: as you can see, #Agent_L is right
The answer to your question "Does this one [Objects.equals] still compare on references, or it compares on contents?" - Objects.equals does some comparisons on the references but it expects the first argument's class to implement equals() in which the comparison of contents is done as well as on reference.
Your second question about the implementation of equals in your tupple-like class having prev and next as its tupple attributes the answer is: your suggested implementation would work only if
prev and next are primitives or if their type implements equals properly.
So if prev for example is of type Foo, then you can use Objects.equals to test the two Foo's only if class Foo implements equals as expected.
Objects.equals just calls it's first arguments .equals method. In java, if you want to be able to test for equality in instances of a class you made, then you have to override the equals method. instance.equals() only uses == if that instances type doesn't override the equals method.
This seems like a silly question but why do we override equals method instead of creating a new method with new name and compare using it?
If I didn't override equals that means both == and equals check whether both references are pointed to same memory location?
This seems like a silly question but why do we override equals method instead of creating a new method with new name and compare using it?
Because all standard collections (ArrayList, LinkedList, HashSet, HashMap, ...) use equals when deciding if two objects are equal.
If you invent a new method these collections wouldn't know about it and not work as intended.
The following is very important to understand: If a collection such as ArrayList calls Object.equals this call will, in runtime, resolve to the overridden method. So even though you invent classes that the collections are not aware of, they can still invoke methods, such as equals, on those classes.
If I didn't override equals that means both == and equals check whether both references are pointed to same memory location?
Yes. The implementation of Object.equals just performs a == check.
You override equals if you are using classes that rely on equals, such as HashMap, HashSet, ArrayList etc...
For example, if you store elements of your class in a HashSet, you must override hashCode and equals if you want the uniqueness of elements to be determined not by simple reference equality.
Yes, if you don't override equals, the default equals implementation (as implemented in the Object class) is the same as ==.
In addition to the main reason, already given in other answers, consider program readability.
If you override equals and hashCode anyone reading your code knows what the methods are for. Doing so tells the reader the criteria for value equality between instances of your class. Someone reading your code that uses equals will immediately know you are checking for value equality.
If you use some other name, it will only confuse readers and cost them extra time reading your JavaDocs to find out what your method is for.
Because equals() is a method of the Object class, which is the superclass of all classes, and due to which it is inherently present in every class you write. Hence every collection class or other standard classes use equals() for object comparsion. If you want your custom class objects to be supported for equality by other classes, you have to override equals() only (since other classes know that for object comparion call equals()). If you are only using your own classes, you might create a new method and make sure everything uses your custom method for comparison.
The equals and hashcode method are special methods, widely used across the java's utility classes specially collection framework, and the wrpper classes e.g. String, Integer have overridden this method, So e.g. if you are placing any Object of your choice which has correct equals and hashcode implementation inside the HashSet, to maintain the property of uniqueness the hashcode will compare with all the existing object in hashset, and if it finds any of the hashcode matching then it looks into the equals method to double check if both are really equal and if that equality check also is pass then incoming object is rejected, but if the hashcode equality check is not passed then hashset will not go for the equals method and straight way place that object into the hashset. So we need to make sure the implementation of equals and hashcode is logically proper.
A class like HashMap<T,U> needs to have some means of identifying which item in the collection, if any, should be considered equivalent to a given item. There are two general means via which this can be accomplished:
Requiring that anything to be stored in a collection must include virtual methods to perform such comparison (and preferably provide a quick means (e.g. hashCode()) of assigning partial equivalence classes).
Require that code which creates the collection must supply an object which can accept references to other objects and perform equivalence-related operations upon them.
It would have been possible to omit equals and hashCode() from Object, and have types like HashMap only be usable with key types that implement an equatable interface that includes such members; code which wishes to use collections of references keyed by identity would have to use IdentityHashMap instead. Such a design would not have been unreasonable, but the present design makes it possible for a general-purpose collection-of-collections type which uses HashMap to be usable with things that are compared by value as well as by identity, rather than having to define a separate types for collection-of-HashMap and collection-of-IdentityHashMap.
An alternative design might have been to have a GeneralHashMap type whose constructor requires specifying a comparison function, and have IdentityHashMap and HashMap both derive from that; the latter would constrain its type to equatable and have its identity functions chain to those of the objects contained therein. There would probably have been nothing particularly wrong with that design, but that's not how things were done.
In any case, there needs to be some standard means by which collections can identify items that should be considered equivalent; using virtual equals(Object) and getHashCode() is a way of doing that.
Question 1
There are Two things.
equals() is Located inside Object class
Collection framework using equals() and hashcode() methods when comparing objects
Question 2
Yes for comparing two Object. but when You comparing two String Objects using equals() its only checking the value.
I know that if a.equals(b), we must have a.hashCode() == b.hashCode() else we get strange results, but I'm wondering if the converse is also required.
More specifically, I have a hashCode() function that uses the field id to calculate the hashCode. However, my equals() function only uses the simple comparison "==" to check for equality. This may seem strange but unless more details are required, it's simply how I've implemented it.
Now the question is will this mess anything up? Specifically for HashSets, but more generally, for any (common) implementations of Set.
The way I understand it, a Set will first check the hashCode then the equals operator to see if a duplicate object exists. In this case, it should work right? If two objects are the same instance, they will produce the same hashCode as well as return true for equals() and thus the Set will only allow the instance to be be added once.
For two separate instances with the same id, the hashCode will be identical but then the equals() operator will return false and thus allow both objects to enter the Set, which is what I hope to accomplish.
Is this a beginner's mistake? Am I missing something? Will this have an unexpected results for any collection types other than Set?
edit:
I guess I should explain myself. I have a Hibernate object FooReference which implements both a hashCode and equals method using the id. This object is guaranteed to always have a unique id. However, before this object is persisted, I use a Foo object which has a default id of -1. So when putting it in a Set (to be saved) I know each Foo is unique (thus the basic == operator). So this Foo which extends FooReference overrides the equals method with a basic ==. For my purposes this should work... hopefully.
Objects are allowed to have the same hashcode without being equal to each other. In fact, it's perfectly valid (though inefficient and a bad idea) to implement hashCode as simply return 0, giving every instance the same hashcode.
All that's required is that if two objects are equal (as determined by the equals method), they have the same hashcode.
However, if your equals method just compares the two objects using == internally, no two (distinct) instances will ever be equal to each other, so there's no point defining your own hashCode and equals methods at all. The default implementations will produce the same behavior.
I am trying to check wether mylist contains a given object or not, where mylist is an ArrayList of type myCustomClass.
If you add an instance of MyCustomClass to the list, and then check if it contains another instance of MyCustomClass, it will always return false, unless you override the equals method in your custom class. The equals method checks if another object is functionally equal to this object.
Make sure to override the hashCode method each time you override the equals method. hashCode should return the same value for two equal objects. Also, equals should be written so that it's symmetric: a.equals(b) if and only if b.equals(a).
Check equals and hashCode in the javadoc of java.lang.Object.
You most likely haven't implemented equals() and hashcode() on myCustomClass. You need to implement them properly and according to contract, see here for details of how.
How does a set differentiate between objects in both Java and C++? Or do sets not differentiate them at all?
Take these for example:
C++
std::set<A> aset;
A a(1, 2); // Assume A has only two elements, and this constructor sets them both
aset.insert(a);
A a2(1, 2); // This would initialise a `A' object to the same values as `a', but a different object
aset.count(a2); // Would this return 1 or 0?
Java
set<A> aset;
A a = new A(1, 2); // Assume A has only two elements, and this constructor sets them both
aset.add(a);
A a2 = new A(1, 2); // This would initialise a `A' object to the same values as `a', but a different object
aset.contains(a2); // Would this return true or false?
In C++ the set depends on operator<() being defined for the class A, or that you supply a comparison object providing strict weak ordering to the set.
For Java it depends on the equals, hashcode contract.
For the Java part,
The method in charge of determining whether two objects are equal is:
public boolean equals(Object other)
Not to be confused with
public int hashCode()
Whose contract states that two equals objects must return the same number, but two objects that returned the same number may be, but are not necessarily, equal.
The default implementation for the equals method is equality by memory address, therefor if class A did not override the equals method the contains method will return false.
To have the set.contains(a2) method return true you must override the equals and hashCode method to comply as so:
public boolean equals(Object other) {
return other instanceof A && ((A) other).elem1 = this.elem1 && ((A) other).elem2 = this.elem2;
}
public int hashCode() {
return elem1 * 31 + elem2;
}
The hashCode is required (assuming you're using a HashSet) for the set to identify where in the internal representation of the set the object may be (i.e. where to look for it).
Search for HashSet\HashMap to understand the internal representation if you're interested.
As for the C++ part, If I remember correctly it depends on the correct operator overloading, but my C++ is rusty at best.
EDIT: I noticed you specifically asked about sets so I'll elaborate a bit more on how that:
While the equals method is what determines equality between two objects, some preliminary steps in the set implementation used (e.g. HashSet or TreeSet) might relay on something extra:
For example, the HashSet uses the hashCode() function to find the internal location the item might be in, so if A did not override/correctly implement the hashCode() function, the set.contains(a2) may return true or false (for default implementation it's non deterministic - depends on memory location and the current capacity of the set).
For a TreeSet internal implementation to correctly find items within it either the items contained must be implement the Comparable interface properly or the TreeSet itself must be supplied with a Comparator instance implemented properly.
for C++, according to set::insert in C++ Reference
Because set containers do not allow for duplicate values, the
insertion operation checks for each
element inserted whether another
element exists already in the
container with the same value, if so,
the element is not inserted and -if
the function returns a value- an
iterator to it is returned
.
They check for the values, unlike Java, which only checks for the address instead.
In Java at least, comparison is done on a hash code, which by default is created from the location of the object in memory. So in the Java part of the question, aset.contains(a2); would return false, as a2 points to a different part of the memory to a.
I'm afraid I can't comment on how C++ works!
Java calls the object's equals method, which, if you haven't overridden it, is the same as calling Object.hashCode().