what is the difference between equals and contains methods - java

I was working with the TreeSet collection with the following code:
import java.util.*;
public class Employee implements Comparable<Employee>{
private int ID;
public Employee(int iD) {
ID = iD;
}
#Override
public int compareTo(Employee obj) {
return this.ID-obj.ID;
}
private static void intoTreeSet() {
Employee e1=new Employee(4);
Employee e2=new Employee(2);
Employee e3=new Employee(1);
Employee e4=new Employee(5);
Employee e5=new Employee(3);
Employee eTemp=new Employee(3);
Set<Employee> set=new TreeSet();
set.add(e1);set.add(e2);set.add(e3);set.add(e4);set.add(e5);
System.out.println("output says: ");
for(Employee e:set){
System.out.print(e.ID+" ~ ");
}
System.out.println();
if(set.contains(eTemp)){
System.out.println("C O N T A I N S !!!");
}
if(e5.equals(eTemp)){
System.out.println("E Q U A L S !!!");
}
}
public static void main(String[] args) {
intoTreeSet();
}
}
Output
output says:
1 ~ 2 ~ 3 ~ 4 ~ 5 ~
C O N T A I N S !!!
I am confused to see the output. I want to know, if it does NOT pass equals case, then how come it pass contains case.
I know that two objects can only be equal if their class overrides equals method and they are equal according to some property. I intentionally did not override equals method to see how contains work. If it were a non-tree based collection lets say an ArrayList it would NOT have passed contains test. Why is it so ? can any one explain this behaviour and clear my confusion.

The javadoc for java.util.TreeSet says:
Note that the ordering maintained by a set (whether or not an explicit comparator is provided) must be consistent with equals if it is to correctly implement the Set interface. (See Comparable or Comparator for a precise definition of consistent with equals.) This is so because the Set interface is defined in terms of the equals operation, but a TreeSet instance performs all element comparisons using its compareTo (or compare) method, so two elements that are deemed equal by this method are, from the standpoint of the set, equal. The behavior of a set is well-defined even if its ordering is inconsistent with equals; it just fails to obey the general contract of the Set interface.
In other words, the implementations of compareTo and equals must be consistent with each other. If they're not, the behaviour of TreeSet will be erratic. It may work, it may not. To understand when it does and when it doesn't you need to look closely at the TreeSet implementation, but since the javadoc is very explicit on the conditions under which TreeSet works, it's really not a good idea to try and subvert it.

The most important point to remember here is, a TreeSet is a SortedSet which performs element comparisons using compareTo (or compare) method.
The Employee class is comparable. From the definition from the docs of the comparable interface,
This interface imposes a total ordering on the objects of each class
that implements it. This ordering is referred to as the class's
natural ordering, and the class's compareTo method is referred to as
its natural comparison method.
So if your compareTo method returns 0, for two instances of the same class, they are considered to be naturally equal by the TreeSet.
The document also says,
It is strongly recommended (though not required) that natural orderings be consistent with
equals. This is so because sorted sets (and sorted maps) without explicit
comparators behave "strangely" when they are used with elements (or
keys) whose natural ordering is inconsistent with equals.
Though it is not defined how "strangely" it can behave.
In our case e5.equals(eTemp) is false, since the equals method is not overridden.
And e5.compareTo(eTemp) is true, so from the set's perspective e5 and eTemp are equal.
To demonstrate this you can perform the below operations:
Employee e1 = new Employee(3);
Employee e2 = new Employee(3);
set.add(e1); // gets added to the set
The below operation will return false because the set considers an equivalent of e2 already to be present in the set, though e1.equals(e2) is false, and the size of the set remains same.
System.out.println(set.add(e2)); // false
So to to be consistent, you could override the equals method, though it is not necessary.

Related

Adding Object to HashSet

I am trying to add an Object (Exception) to a Set, however it adds every Exception, eventhough some are duplicates.
debug
In my case duplicates are Exceptions which have the same Detail message.
How do I properly add the Exceptions to the HashSet only if the Exception.getDetails() doesn't already exist?
Is there another approach than a HashSet?
Performance is a criteria here, quadratic solutions (O(n^2))are not an option.
You have a few options:
override hashcode and equals in your exception class
use a TreeSet with a custom Comparator
use a Map<String, Exception> where the key is the getDetails() result (for example, a HashMap)
You need to override how the Execptions are compared so it recognises duplicates the way you want. You can't do this for a HashSet but you can for TreeSet e.g.
Set<Exception> exceptions = new TreeSet<>(Comparator.comparing(Object::toString));
This example compares the toString which is the exception type and message in most cases.
If you really want to use a HashSet you need to wrap the Exception in a class which implements hashCode and equals the way you want.
If all you care about is the type and message you can store just the toString of each exception
final Set<String> exceptions = new HashSet<>();
public void addException(Exception e) {
exceptions.add(e.toString());
}
You need to redefine equals and hashCode methods.
If the detail is a String you can redefine them as follow
public boolean equals(Object obj) {
if (!(obj instanceof YourException)) {
return false;
}
return getDetail().equals(((YourException) obj).getDetail());
}
public int hashCode() {
return getDetail().hashCode();
}
Consider this code as a base to program. You have to check for null values for example.
Once redefined equals and hashCode inserting YourException in a TreeSet is an operation done in O(log(n)) where n is the size of the set, from javadoc:
This implementation provides guaranteed log(n) time cost for the basic operations (add, remove and contains).

Java clone() inquiry

Having written the code below, I was wondering why clone() doesn't return the same hashcode for each additional instance. Am I doing something wrong?
public class Accessor implements Cloneable {
public static void main(String[] args) {
Accessor one = new Accessor();
Accessor two = one.clone();
System.out.println("one hahcod " + one.hashCode()
+"\ntwo hashcode " + two.hashCode());
}
public Accessor clone(){
try{
return (Accessor)super.clone();
}
catch (CloneNotSupportedException err){
throw new Error("Error!!!");
}
}
}
Since Accessor does not override hashCode, you will get the default implementation of Object.hashCode. This has implementation-defined semantics but will basically cast the address of the object to an integer, such that distinct object instances will have different hashCodes.
See What is the default implementation of `hashCode`? for more information on the above.
Note that if you are going to implement hashCode, you should also implement equals. For a good reference on equals and hashCode, read Joshua Bloch's Effective Java (or see Best implementation for hashCode method)
Because it is a different object. You are invoking the cloning inherited from Object in this case. For each new object you will have a different dashcode. If you open the source code of Object in java what you will find there is the following:
public native int hashCode();
public boolean More ...equals(Object obj) {
return (this == obj);
}
The key point here is that once you clone an object A clone of B A==B will always return false.
Then if you read the hashcode documentation it states the following :
If two
objects are equal according to the equals(Object) method, then calling
the hashCode method on each of the two objects must produce the same
integer result. It is not required that if two objects are unequal
according to the equals(java.lang.Object) method, then calling the
hashCode method on each of the two objects must produce distinct
integer results. However, the programmer should be aware that
producing distinct integer results for unequal objects may improve the
performance of hashtables.
The clone method creates a shallow copy of your first object but your Accessor class has no instance field and does not override hashCode method, as a consequence the instances of this class get the default behaviour from Object class for hashCode. This behaviour is similar as calling System#identityHashCode with your object as parameter.

How to sort a list of class objects on one of its fields?

I have a list of DataPoint objects. The class definition is:
public static class DataPoint
{
public Comparable X;
public Comparable Y;
public Comparable Z;
public String text;
...
}
"list" is an ArrayList of DataPoint objects. How do I sort list only on the X value? Would Collections.sort(list, comparator) be used here?
Yes, you should create specific comparator for each field. Example:
Comparator<DataPoint> compByX = new Comparator<DataPoint>() {
#Override
public int compare(DataPoint left, DataPoint right) {
return left.X.compareTo(right.X);
}
};
Collections.sort(list, compByX);
You have two choices:
implement Comparable<DataPoint> for your DataPoint class
write a custom comparator that implements Comparator<DataPoint> and then use Collections.sort
First solution is meaningful if you want to give a natural ordering on your objects (which will be the most used one). Usually it's the one you use first while you use comparators just when you need additional orderings.
They both behave in the same way but Comparable<T> is inherently attached to the object as it is its default comparison algorithm. Whenever sorting is involved the default one will be used unless you specify another one.
class DataPoint implements Comparable<DataPoint> {
#Override
public int compareTo(DataPoint o) {
return X.compareTo(o.X);
}
}
Mind that when you need to compare objects you usually need also other operations on them so take care of overriding hashCode() and equals(Object o). The latter is used in sorting as documentations states:
The natural ordering for a class C is said to be consistent with equals if and only if e1.compareTo(e2) == 0 has the same boolean value as e1.equals(e2) for every e1 and e2 of class C. ... It is strongly recommended (though not required) that natural orderings be consistent with equals.
This means that if you just compare X variable then two different DataPoint objects with same X will be considered equal with respect to compareTo. This can lead to strange situations.

Possible bug in java.util.Set API [closed]

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 10 years ago.
java.util.Set API states:
sets contain no pair of elements e1 and e2 such that e1.equals(e2)
But as far as I understand TreeSet uses Comparable/Comparator to determine if e1 and e2 are duplicates. Am I missing something?
If compareTo is consistent with equals (which it should), it doesn't matter whether a TreeSet uses compareTo or equals to determine equality. From the JavaDoc:
Note that the ordering maintained by a set (whether or not an explicit
comparator is provided) must be consistent with equals if it is to
correctly implement the Set interface. (See Comparable or Comparator
for a precise definition of consistent with equals.) This is so
because the Set interface is defined in terms of the equals operation,
but a TreeSet instance performs all element comparisons using its
compareTo (or compare) method, so two elements that are deemed equal
by this method are, from the standpoint of the set, equal. The
behavior of a set is well-defined even if its ordering is inconsistent
with equals; it just fails to obey the general contract of the Set
interface.
This program will print 'true', even though equals always returns false. But the bug would be in the fact the compareTo and equals are inconsistent for A, and is not a bug in TreeSet.
class A implements Comparable<A> {
public int compareTo(A a) {
return 0;
}
public boolean equals(Object other) {
return false;
}
public static void main(String[] args) {
TreeSet<A> set = new TreeSet<A>();
set.add(new A());
System.out.println(set.contains(new A()));
}
}
TreeSet use Comparator. Do you know it actually maintains elements by using map.
public TreeSet() {
this(new TreeMap<E,Object>());
}
As you said Sets contain no pair of elements e1 and e2 such that e1.equals(e2). That is why Comparator has equals method.
No matter how we are going to use Set it depends with Comparator. If we use our own comparator, we can use Set as List but normally we don't do that. If we add our own Object in to TreeSet must pass Comparator when initializing TreeSet or object must be implemented by Comparable
Comparable
A comparable object having capability to compare itself with another object
Comparator Used for comparing two different objects.
public int compare(Employee o1, Employee o2) {
// if sort by name
return o1.getName().compareTo(o2.getName());
}
public boolean equals(Object obj) {
// equals with ID
Employee e = (Employee)obj;
return this.getId().equals(e.getId());
}
Here is Java Sorting: Comparator vs Comparable a tutorial.
Your assumption is wrong; TreeSet doesn't use Comparable/Comparator to determine if e1 is equal to e2.
Equals method is part of Object class and used to decide if two elements of collection are same or not. i.e. e1.equals(e2)
But Comparable/Comparator interface is used to decide if an element is greater than, equal or less than other element. And this is used during sorting. So only thing you need to ensure is that equals and compareto methods are consistent.
So to compare two objects equals method is used and comparator/comparable is used during sorting
EDIT
Below is the method definition from JDK 6; equals method of AbstractSet. TreeSet extends AbstractSet
public boolean equals(Object obj)
{
if(obj == this)
return true;
if(!(obj instanceof Set))
return false;
Collection collection = (Collection)obj;
if(collection.size() != size())
return false;
try
{
return containsAll(collection);
}
catch(ClassCastException classcastexception)
{
return false;
}
catch(NullPointerException nullpointerexception)
{
return false;
}
}
http://docs.oracle.com/javase/6/docs/api/java/util/AbstractSet.html

Sort a Vector of custom objects

I'm trying to sort a Vector in java but my Vector is not a vector of int, it is a vector of objects
the object is:
public MyObject() {
numObj = 0;
price = new Price();
pax = new Pax();
}
so I have a Vector of MyObject and I want to order it by numObject, how do i do it, I'm new in java?
Thank you so much for all your help.
I assume you are using Collections.sort(..). You have two options:
make your class implement Comparable
when sorting, create a custom Comparator
An example for implementing Comparable would be:
public class MyObject implements Comparable<MyObject> {
// ..... other fields and methods
public int compareTo(MyObject other) {
return numObj - other.getNumObj();
}
}
This will mean the items are sorted in ascending order. If you want other, custom sorting, you can create a Comparator that defines the custom comparison and pass it as an argument to Collections.sort(..);
To sort a vector of object, first the class MyObject must implement Comparable and implement method compareTo(Object), then use Collections.sort(Vector)
class MyObject implements Comparable<MyObject> {
public int compareTo(MyObject a) {
//return either 1, 0, or -1
//that you compare between this object and object a
``}
}
//and in your logic write this line
Collections.sort(myVector);
check the JavaDoc of Vector
Implementing Comparable will work fine. However, it should be done with caution in order to prevent future bugs. The implementations above are great for sorting, but if you later decide to put your MyObject instances into a SortedSet (such as TreeSet), you will have an unexpected behavior. Items which are not identical will collide with each other if they have the same value of the numObject data member. This happens because sorted sets use the Comparable implementation (by default) in order to determine equality.
See http://eyalsch.wordpress.com/2009/11/23/comparators/ for details.

Categories

Resources