Please don't mind the my convention mistakes
class test implements Comparable<test>
{
int id;
String name;
public test(int id,String name)
{
this.id=id;
this.name=name;
}
#Override
public int compareTo(test o) {
if(this.id>o.id)
return 1;
else if(this.id==o.id)
return 0;
else
return -1;
}
}
class le
{
public static void main(String[] args) {
TreeMap<test,test> p=new TreeMap<test,test>();
p.put(new test(1,"sad"), new test(3121, "adweq"));
p.put(new test(2, "asds"),new test(3123,"awdq"));
p.put(new test(23,"akjdb"),new test(23123,"dqWQDD"));
Set<Map.Entry<test,test>> s=p.entrySet();
Iterator <Map.Entry<test, test>> i=s.iterator();
while(i.hasNext())
{
Map.Entry<test, test> m=i.next();
System.out.println(m.getKey().id);
System.out.println(m.getValue().name);
}
System.out.println(p.containsKey(new test(1,"sad")));//returning true
System.out.println(p.containsValue(new test(3123,"awdq")));//why it is returning false
}
}
here i have made a treemap,and wanted to know why does in containsvalue method it return false? whereas i have implemented comparable interface>
a compareTo() method is not enough - you need to implement an equals() method (and is recommended to also override hashCode() when you override equals()). Here's how:
class test implements Comparable<test>
{
int id;
String name;
public test(int id,String name)
{
this.id=id;
this.name=name;
}
#Override
public int compareTo(test o) {
if(this.id>o.id)
return 1;
else if(this.id==o.id)
return 0;
else
return -1;
}
#Override
public boolean equals(Object o) {
if (o == null)
return false;
if(!this.getClass().equals(o.getClass())
return false;
test that = (test) o;
return this.compareTo(that) == 0;
}
#Override
public int hashCode() { return id; }
}
Side note
Why does equals() use getClass().equals(o.getClass()) rather than (o instanceof test)?
Let us assume there is a subclass of the test class called test2 and that t1 and t2 are objects of type test, test2 (respectively).
If test2 overrides equals() then t1.equals(t2) can yield different result than t2.equals(t1) if equals() in test were implemented using instanceof. This violates the equals() contract (specifically, the symmetric requirement).
Because your class test doesn't override equals() and hashCode(), something like
#Override
public boolean equals(Object o) {
if (o instanceof test) {
test t = (test) o;
return t.id == o.id;
}
return false;
}
#Override
public int hashCode() {
return Integer.valueOf(id).hashCode();
}
Assuming that id equality is sufficient. Additionally, test is a poor class name. The Java naming convention would be Test but that's also a poor name. Maybe, EqualityTest (so it has some meaning).
You need to override Object.equals in your Test class in order to check for equality between new test(3123,"awdq") and another instance of new test(3123,"awdq").
It is also recommended to override Object.hashCode when overriding equals.
Related
In this exercise, I need to create a equals() method for a Drink class. Two drinks are the same if they have the same name and same size. I am receiving false from testing the method, even though I'm certain it should be true.
The main code:
public class Drink {
private String name;
private double size;
public Drink(String name, double size)
{
this.name = name;
this.size = size;
}
public String getName()
{
return name;
}
public double getSize()
{
return size;
}
//I tried to stringify the double values
public boolean equals(Drink a, Drink b){
String q = String.valueOf(a.getSize());
String w = String.valueOf(b.getSize());
if(q.equals(w) && a.getName().equals(b.getName())){
return true;
}
else{
return false;
}
}
}
The tester Code:
public class DrinkTester
{
public static void main(String[] args)
{
Drink one = new Drink("Soda", 12);
Drink two = new Drink("Soda", 12);
Drink three = new Drink("Soda", 20);
System.out.println(one.equals(two));
System.out.println(one.equals(three));
}
}
You need to override the equals method, if you use the
#Override annotation you'll see if you're doing it right.
public boolean equals(Object obj) {
return (this == obj);
}
That is the Object one, so yours might for example look like:
#Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Drink drink = (Drink) obj;
return this.size.equals(drink.size)
&& this.name.equals(drink.name);
}
you'll also have to override your hashCode if you want your code to work optimally.
(And i've only recently noticed that if you use Objects.hash in your overridden hashCode method, your overridden equals method won't get used, the Objects one will get used instead)
Suppose, I have a Class, call it Student. The student class has an element, an int called Id. I want to override equals, so that if a Student is compared to an Integer the method returns true. like:
public class OverrideTest {
public static void main(String[] args) {
Student a = new Student();
a.setId(5);
System.out.println(a.equals(5));
}
public static class Student {
private int id;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#Override
public boolean equals(Object o) {
if (o instanceof Student) {
Student a = (Student) o;
if (a.getId() == this.getId())
return true;
}
if (o == this)
return true;
if (o instanceof Integer) {
int id = (Integer) o;
if (id == this.getId())
return true;
}
return false;
}
}
}
Is there a way to signal the IDE that this is okay and no warnings have to be sent?
This returns true, but with a syntax warning in IDE.
As Yshavit noted, while you can do what you're trying to do, it's extremely dangerous because it breaks the symmetry of the .equals(...) relation. For example, consider:
Student s = new Student(5);
Integer i = new Integer(5);
if(s.equals(i)) System.out.println("s equals i");
else System.out.println("s doesnt equal i");
if(i.equals(s)) System.out.println("i equals s");
else System.out.println("i doesnt equal s");
Output:
s equals i
i doesnt equal s
Logically this doesn't make any sense. There is probably a way to convince your IDE to ignore the warning here, but you should almost certainly heed it and not make Students able to equal Integers.
The equals(..) method is used internally in containers such as HashSets, so this method would cause have erratic behavior in built-in structures.
More about overriding equals(...)
I am writing a comparable class. I have overridden compareTo method to sort my objects based on date in descending order.
public class Employee implements Comparable
{
private Timestamp joinDate;
public Timestamp getJoinDate()
{
return joinDate;
}
public void setJoinDate(Timestamp joinDate)
{
this.joinDate = joinDate;
}
#Override
public int compareTo(Employee a)
{
//sort employess based on join date desc
return a.getJoinDate().compareTo(this.getJoinDate());
}
}
My Sonar is complaing to override equals method.
How do I override equals method here.
If you want to override the method compareTo, you have to use the same signature. The actual signature uses an Object parameter:
#Override
public int compareTo(Object o)
{
return ((Employee) o).getJoinDate().compareTo(joinDate);
}
Note that you have to explicitly cast the object obj to Employee, otherwise you won't be able to call its method getJoinDate().
Edit: If you want to override the equals() method you can return the result of comparing the attributes joinDate:
#Override
public boolean equals(Object obj)
{
return joinDate.equals(((Employee) obj).getJoinDate());
}
Note: It's not necessary to call getJoinDate() inside the Employee class, so you can just do:
return ((Employee) o).joinDate.compareTo(joinDate);
or
return joinDate.equals(((Employee) obj).joinDate);
Include this on your class (when you override equals, you have to also override hashCode):
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((joinDate == null) ? 0 : joinDate.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;
Snippet other = (Snippet) obj;
if (joinDate == null) {
if (other.joinDate != null)
return false;
} else if (!joinDate.equals(other.joinDate))
return false;
return true;
}
There is a difference between what what is indicated by .equals() == true and .compareTo(...) == 0. equals() method is intended to check whether two objects are equal, while compareTo is intended to set a relation order between elements, whether one is greater then the other, other is greater then the first one, or none of this is applicable which is typically means that objects are equal.
Unless you have a very good reason, you should override both equals and compareTo. An example of very good reason is BigDecimal class where equals compares both value of the object and its scale, while compareTo compares only values.
For your case, I'd override equals() like this:
#Override
public int hashCode() {
return this.getJoinDate().hashCode();
}
#Override
public boolean equals(Object obj) {
//correct argument check
if (!(obj instanceof Employee)) {
return false;
}
//check nulls
if (obj == null) {
return false;
}
Employee other = (Employee) obj;
if (this.getJoinDate() == null) {
return other.getJoinDate() == null;
}
return this.getJoinDate().equals(other.getJoinDate());
}
If I run the below code then the output is 2 which means that the set contains 2 elements. However I think that set should contain 1 since both the objects are equal based on hashcode() value as well as .equals() method.
Seems like some obvious mistake in my understanding ?
package HELLO;
import java.util.HashSet;
import java.util.Set;
public class Test {
public static void main(String[] args) throws Exception {
Set<Alpha> s = new HashSet<Alpha>();
Alpha a1 = new Alpha();
Alpha a2 = new Alpha();
s.add(a1);
s.add(a2);
System.out.println(s.size());
}
}
class Alpha {
int a = 10;
public int hashcode() {
return a;
}
public boolean equals(Object obj) {
return (obj instanceof Alpha && ((Alpha) obj).a == this.a);
}
public String toString() {
return "Alpha : " + a;
}
}
Your hashcode method does not override the Object class's hashCode method and thus your equals method breaks contract since it doesn't agree with the hashCode results, and you can have objects that are "equal" but have different hashCodes.
Remember: You should always use the #Override annotation when overriding methods as this will help you catch this and similar errors.
#Override // ** don't forget this annotation
public int hashCode() { // *** note capitalization of the "C"
return a;
}
Also, you will want to improve your code formatting, especially when posting code here for our review. We will be able to better understand your code and help you if it conforms to standards (that's why standards exist). So try to keep your indentations consistent with all code lines that are in the same block indented at the same level, and you will want to be sure that base level code, including imports, outer class declarations and its end curly brace, is flush left:
import java.util.HashSet;
import java.util.Set;
public class Test {
public static void main(String[] args) throws Exception {
Set<Alpha> s = new HashSet<Alpha>();
Alpha a1 = new Alpha();
Alpha a2 = new Alpha();
s.add(a1);
s.add(a2);
System.out.println(s.size());
}
}
class Alpha {
int a = 10;
#Override
public int hashCode() {
return a;
}
public String toString() {
return "Alpha : " + a;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Alpha other = (Alpha) obj;
if (a != other.a)
return false;
return true;
}
}
For a beautiful review on this, please read: Overriding equals and hashCode in Java
The #Overrides annotation is to override the method with the same name in the super class".
#Override
public int hashCode() {
return a;
}
#Override
public boolean equals(Object obj) {
return (obj instanceof Alpha && ((Alpha) obj).a == this.a);
}
#Override
public String toString() {
return "Alpha : " + a;
}
your method hashcode should be named hashCode (capital letter "C").
If you plan on overriding methods you should use the #Override annotation.
If you had used that annotation, you'd have noticed the problem earlier as the code wouldn't have compiled.
Please review code:
/* Run1.java */
package test;
import java.util.Iterator;
import java.util.SortedSet;
import java.util.TreeSet;
public class Run1
{
static public void main(String[] args)
{
SortedSet<TestClass> s = new TreeSet<TestClass>();
s.add( new TestClass("name1", 100) );
s.add( new TestClass("name2", 10) );
s.add( new TestClass("name3", 1) );
s.add( new TestClass("name4", 10) );
s.add( new TestClass("name5", 100) );
Iterator<TestClass> it = s.iterator();
while(it.hasNext())
{
TestClass t = it.next();
System.out.println( t.name+' '+t.value );
}
}
}
/* TestClass.java */
package test;
public class TestClass implements Comparable<TestClass>
{
public String name;
public int value;
public TestClass(String name, int value) {
this.name = name;
this.value = value;
}
public int compareTo(TestClass o)
{
return this.value - o.value;
}
public boolean equals(Object o)
{
if (!(o instanceof TestClass))
return false;
TestClass n = (TestClass)o;
return this.name.equals(n.name);
}
public int hashCode()
{
return 31*name.hashCode();
}
public String toString()
{
return name;
}
}
Print out
name3 1
name2 10
name1 100
as i see because compareTo used for checking to equality (when returned 0). But i need check for unique by field TestClass.name and only sort by TestClass.value
The result of compareTo() and equals() need to be compatible in this case, which means that you need to take into account equality in the comparison. For example:
public int compareTo(TestClass o)
{
return (this.value == o.value) ? this.name.compareTo(o.name) : this.value - o.value;
}
which introduces a sub-order on name for objects with the same value, making the result compatible with your equals() implementation.
how about hacking the compareTo method as follows:
public int compareTo(TestClass o)
{
if (this.name != null && this.name.equals(o.name)) {
return 0;
}
return this.value - o.value;
}
This should do equality checks on name (remove duplicates) while sorting on value
If I understand correctly, then what you want is for your compareTo to always implement the "natural order" for the class. This means the way the client of the class would expect the class the behave. Contractually, compareTo should be consistent with equals, which is why I always implement equals as:
return compareTo(obj)==0;
This guarantees consistency.
Then, if you want another sort order, you should implement another class that implements Comparable. In this way you can have class consistency and separate sort orders.
Write comparator which compares TestClass objects.
public class TVComparator implements Comparator<TestClass> {
public int compare(TestClass o1, TestClass o2) {
if (o1.name.equals(o2.name)) return 0;
return o1.value - o2.value;
}
}
For sake of simplicity I have omitted any checks for null values.