What happen if hascode function is false while equal function is true - java

I am thinking about a question if hashcode function is false but equal function is true, what will happen? For example:
public class Demo {
private int age;
private String name;
//getter
//setter
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Demo demo = (Demo) o;
return age == demo.age &&
Objects.equals(name, demo.name);
}
#Override
public int hashCode() {
return Objects.hash(age, name)
+ (new Random().nextInt(1000));
}
}

What will happen is that instances of the Demo class will behave incorrectly when used as hash keys.
Here is one example of what will happen.
Demo demo = new Demo(...);
HashSet<Demo> set = new HashSet<>();
set.add(demo);
System.out.println(set.contains(demo)); // will print false 99.9% of the time.
In other words, the HashSet will lose track elements from the perspective of the contains method. (But they will still be there if you iterate the set. And you will probably be able to add the same Demo object twice ... leading to the set containing duplicates.)

Related

How to correctly implement equals(), hashCode() if the values are within a range of each other?

The criteria is that equals() method where the objects are considered equal if the value of the double variable is within +/- 10 of the other object's value of the double variable.
I'm not sure how to correctly implement hashCode() so that the hashCode would be equal if it satisfies the conditions of the equals() method.
I would really appreciate your input! Thanks!
public class Test
{
private double value;
private boolean isEqualValues (final double valueOne, final double valueTwo)
{
if(valueOne == valueTwo)
{
return true;
}
else if((valueOne - valueTwo <= 10) &&
(valueOne - valueTwo >= -10))
{
return true;
}
else
{
return false;
}
#Override
public boolean equals(final Object o)
{
if (this == o)
{
return true;
}
if (o == null || getClass() != o.getClass())
{
return false;
}
Test test = (Test) o;
if(isEqualValues(test.value, value))
{
return true;
}
else
{
return false;
}
}
//How to implement hashCode()
#Override
public int hashCode()
{
//unsure how to correctly implement hashCode() so that the hashCode would be equal if it
//satisfies the conditions of the equals() method above
}
}
There's no way to consistently implement this, since equals() demands transitivity:
It is transitive: for any non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
new Test(1), new Test(9) and new Test(14) would fail that test (assuming a trivial one-argument constructor that assigns its argument to value).
One way to work around that is to not check for the absolute distance, but "categorize" your objects using some formula, for example take the floor of value / 10 and compare that.
This way some "close" values like new Test(9) and new Test(11) would compare as not-equal, but other than that you'd get a similar result to what you described.
private long getEquivalenceGroup() {
return Math.floorDiv((long) value, 10);
}
#Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Test test = (Test) o;
return test.getEquivalenceGroup() == this.getEquivalenceGroup();
}
#Override
public int hashCode()
{
return Long.hashCode(getEquivalenceGroup());
}
As long as getEquivalenceGroup() is implemented in a stable manner this will produce "groups" of slightly different objects that still compare as equal and has a valid hashCode() implementation.
Note: if you want a comparison as described in the question but you don't necessarily need it to be returned by equals() then adding a boolean isClose(Test other) is perfectly fine. The only problem is you are trying to implement the equals method specifically with that semantic.
You can't and you shouldn't.
You should implement a comparator and do such operations using that.

Java "contains" not working properly

My class:
public class UserProgressModel {
private String email;
public UserProgressModel(String pEmail) {
super();
this.email = pEmail;
}
#Override
public boolean equals(Object x) {
if (x != null && x instanceof UserProgressModel
&& ((UserProgressModel) x).email.equals(this.email) == true) {
return true;
}
if (x != null && x instanceof String
&& x.equals(this.email) == true) {
return true;
}
return false;
}
#Override
public int hashCode() {
int hash = 7;
hash = 17 * hash + (this.email != null ? this.email.hashCode() : 0);
return hash;
}
}
And after putting some objects via gson:
UserProgressModel[] userProgressArray;
List<UserProgressModel> retUserProgress = new ArrayList<>();
userProgressArray = gs.fromJson(fileContents,
new TypeToken<UserProgressModel[]>() {
}.getType());
for (UserProgressModel ele : userProgressArray) {
if (ele != null) {
retUserProgress.add(ele);
}
}
I am unable to get true for the following code:
retUserProgress.contains("test#test.com");
I looped thru the array to verify that one object has the email.
Am I doing right? I think I have overridden the equals & hashcode.
Your equals implementation is incorrect. If you look at the contract for equals the implementation must be symmetric:
... for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
In your case you have a list of UserProgressModel objects, but you are trying to compare against a String. While you've implemented UserProgressModel.equals(String) you would still need to have String.equals(UserProgressModel) return the correct result. Since you cannot do that this implementation will never work in all cases. What you should do is two things:
Remove the check in equals for String because it will never work.
Use a mock object to check in the collection:
retUserProgress.contains(new UserProgressModel("test#test.com"));
As long as your equals method is correct within your own type (UserProgressModel.equals(UserProgressModel)) this should fix your issue.
You cannot check if the retUserProgress contains e-mails because it does not. The ArrayList contains objects of Class: UserProgressModel, thus you can check if the ArrayList contains a 'UserProgressModel'.
What you would like to do is the following
private boolean containsEmail(List<UserProgressModel> retUserProgress, String email) {
boolean result = false;
for (UserProgressModel object : retUserProgress) {
if (object.equals(email))
result = true;
}
return result;
}
And the call the method like so:
containsEmail(retUserProgress, "test#test.com"); //This will return a true or false, depending if the ArrayList retUserProgress contains the email
i have tested your code in ideone
and it's working
true
UserProgressModel model=new UserProgressModel("test#test.com");
System.out.print(model.equals("test#test.com"));
false
UserProgressModel model=new UserProgressModel("test#test.com");
System.out.print(model.equals("test#test.co"));
try to compare with new object
retUserProgress.contains(new UserProgressModel("test#test.com"))
result
if you don't wanna compare with a new UserProgressModel you need to create your own list type that when it compare two object (UserProgressModel,string) it creates a new UserProgressModel and pass that email for it

I have two arrayList<myObject>, I'm not able to use equals() to be able to compare them correctly. Why?

I have two arrayLists<myObject>, where myObject is an object of a custom class I've created. I want to be able to compare those arrayLists using the equals() method.
After reading and looking for answers, I've read that certain objects like int[] are only considered equal by the equals() method when they are referencing the same thing.
To fix that, I tried to override the equals method in my custom object. My objects have 3 atributes (all basic types), so my equals method now returns true if all the 3 atributes are equal to those of the object compared, and false otherwise. However, comparing the arraylists still doesn't work. What am I doing wrong?
Excuse me for explaining the code instead of posting it, I do it because the variables and names aren't in English.
EDIT: Ok, here's the code. Compra is my custom class; cantidad,concepto and id are its atributes.
#Override
public boolean equals(Object obj) {
boolean result = true;
if (obj == null) {
result = false;
}else{
Compra comprobada = (Compra) obj;
if(!(this.id == comprobada.getId())){
result = false;
}
if(!(this.cantidad == comprobada.getCantidad())){
result = false;
} if(!this.concepto.equals(comprobada.getConcepto())){
result = false;
}
}
return result;
}
Based on this one :
How can I check if two ArrayList differ, I don't care what's changed
If you have implemented your custom object equals correct (you actually override it and have your one) and the size of the arrayList is the same and each of the pair of the objects is equal then it will return equal. In other words what you are trying to do is totally correct but your arrayLists are not actually having exactly the equal objects in exact order.
Make sure that your equal is called when you check for collection equality by doing a System.out.println() to investigate what is going on.
If you don't mind please send the equals of your object.
I run your code in an isolated example and works fine (outtputs true) - I improved the equals method so it doesn't do so many if checks as if only one of them is not equal it should return false.
class stackoverflow {
public static void main(String args[]){
ArrayList<Compra> array1 = new ArrayList<>();
ArrayList<Compra> array2 = new ArrayList<>();
array1.add(new Compra(1,2,"test"));
array2.add(new Compra(1,2,"test"));
System.out.println(array1.equals(array2));
}
}
class Compra {
int id;
int cantidad;
String concepto;
public Compra(int id, int cantidad, String concepto){
this.id = id;
this.cantidad = cantidad;
this.concepto = concepto;
}
public boolean equals(Object obj) {
if (obj == null) {
return false;
}else{
Compra comprobada = (Compra) obj;
if(!(this.id == comprobada.getId())){
return false;
}
if(!(this.cantidad == comprobada.getCantidad())){
return false;
}
if(!this.concepto.equals(comprobada.getConcepto())){
return false;
}
}
return true;
}
public int getId() {
return id;
}
public int getCantidad() {
return cantidad;
}
public String getConcepto() {
return concepto;
}
}
Some things to check:
Are you sure you don't change the order of the things in ArrayList??:
Do you print to make sure that these equals checks happen and return true or false as expected?
Are you sure that concepto Strings are exactly the same, with the same case and don't contain extra spaces etc?
As you haven't posted code i suggest you to check into Comparable class and method compareTo and how to use it for custom classes.

How to compare the data of two same Objects in Java

I have a class
MyData
and its object
myData
In that Class MyData .. there are multiple fields
like
int id
String name
String desc
etc ..
Now i have two objects of this class ..
Is it possible to check that if the data of these two object are all the same , Like both objects have the same Id ,same Name ,same Desc ... Without checking each and every field of this object ..(i.e without checking the id,name,desc of Each object myself) As there are dozens of fields of this object .
I am using JAVA with GWT
Some implementation i came across.. Not sure if this is some thing possible .valid
private static String oldSequence = "";
boolean changed(TestSequence sequence) {
String newSequence = serializeToString(sequence);
boolean changed = !newSequence.equals(oldSequence);
oldSequence = newSequence;
return changed;
}
private static byte[] serialize(Object obj) throws IOException {
ByteArrayOutputStream b = new ByteArrayOutputStream();
ObjectOutputStream o = new ObjectOutputStream(b);
o.writeObject(obj);
return b.toByteArray();
}
private static String serializeToString(Object obj) {
try {
return new String(serialize(obj));
} catch (Exception ex) {
return "" + ex;
}
}
Thanks
You should override hashCode() and equals() method. you can generate these from IDE.
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof MyData)) return false;
MyData myData = (MyData) o;
if (id != myData.id) return false;
if (!desc.equals(myData.desc)) return false;
if (!name.equals(myData.name)) return false;
return true;
}
#Override
public int hashCode() {
int result = id;
result = 31 * result + name.hashCode();
result = 31 * result + desc.hashCode();
return result;
}
Now you can compare the objects. That's it.
Conventional way is to override equals and hashCode methods. Java standard libraries, for instance Map s, List s, Set s use the equals and hashCode functions for equality testing. The code below also null-safe;
Here is the code for your case;
public class MyData {
int id;
String name;
String desc;
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MyData myData = (MyData) o;
if (id != myData.id) return false;
if (desc != null ? !desc.equals(myData.desc) : myData.desc != null) return false;
if (name != null ? !name.equals(myData.name) : myData.name != null) return false;
return true;
}
#Override
public int hashCode() {
int result = id;
result = 31 * result + (name != null ? name.hashCode() : 0);
result = 31 * result + (desc != null ? desc.hashCode() : 0);
return result;
}
}
and you can test the equality by;
....
Mydata d1 = new...
Mydata d2 = new...
boolean areTheyEqual = d1.equals(d2);
However if you are not allowed to make a compare field by field then you can use byte arrays, there is no need to convert them to strings.
.....
public boolean equals(Object other){
if (this == other) return true;
if (other == null || getClass() != other.getClass()) return false;
byte[] bytesThis = serialize(this);
byte[] bytesOther = serialize(other);
if(bytesOther.length != bytesThis.length) return false;
return Arrays.equals(bytesThis, bytesOther);
}
public static byte[] serialize(Object obj) throws IOException {
ByteArrayOutputStream b = new ByteArrayOutputStream();
ObjectOutputStream o = new ObjectOutputStream(b);
o.writeObject(obj);
return b.toByteArray();
}
...
GWT doesn't make a difference to your requirement.
There is no direct way.
You have to define your equality to check weather they are equal or not. That is overriding equals() method.
#Override
public boolean equals(Object obj) { ...
Before doing:Right way to implement equals contract
Like everyone else is saying, you should override the equals() and hashCode() methods.
Note that you don't have to do this manually. In Eclipse you can simply click on Source/generate hashCode() and equals() and it will do the work for you. I am sure other IDEs have similar feature as well.
If you don't want to add any more code when you add a new field, you can try iterating over fields.
You said "Without checking each and every field of this object ..(i.e without checking the id,name,desc of Each object myself) ", I couldn't figure out whether you don't want to check for each field for equality, or don't want to WRITE a check for each field for equality. I assumed the latter since you tried to add an equality comparison method by using bytewise checks.
Anyways, the code to check each field follows. You can copy/paste to any object. If, in the future, you want some fields to be checked and some not, you can use annotations.
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MyData myData = (MyData) o;
Field[] fields = this.getClass().getDeclaredFields();
for(Field field:fields){
Object o1 = null;
Object o2 = null;
try {
o1 = field.get(this);
o2 = field.get(o);
} catch (IllegalAccessException e) {
return false;
}
if(o1 == null && o2 != null) return false;
if(o2 == null && o1 != null) return false;
if(o2 == null && o1 == null) continue;
if(!o2.equals(o1)) return false;
}
return true;
}
No.
You have to override the equals() method and compare the objects in that.
Override the equals method of the object in MyData and check the fields independently.
Serialize your objects and compare the results!
You just should be wise in selection of your serialization method.
Override hashCode() and equals() methods
hashCode()
This method provides the has code of an object.
Basically the default implementation of hashCode() provided by Object is derived by mapping the memory address to an integer value. If look into the source of Object class , you will find the following code for the hashCode.
public native int hashCode();
It indicates that hashCode is the native implementation which provides the memory address to a certain extent. However it is possible to override the hashCode method in your implementation class.
equals()
This method is used to make equal comparison between two objects. There are two types of comparisons in Java. One is using “= =” operator and another is “equals()”. I hope that you know the difference between this two. More specifically the .equals() refers to equivalence relations. So in broad sense you say that two objects are equivalent they satisfy the equals() condition.

getting an object from an arrayList with objects attribute

I have 2 classes.
public class klass1 {
String bir;
String myID;
klass1(String bir, String myID)
{
this.bir=bir;
this.myID=myID;
}
}
.
import java.util.*;
public class dd {
public static void main(String[] args) {
ArrayList<Object> ar=new ArrayList();
ar.add(new klass1("wer","32"));
ar.add(new klass1("das","23"));
ar.add(new klass1("vz","45"));
ar.add(new klass1("yte","12"));
ar.add(new klass1("rwwer","43"));
ar.remove(new klass1("vz","45"));//it's not worked!!!
System.out.println(ar.size());
}
}
What I want is removing or getting an object from array list with object's second attribute. How can I do that? Is there an easy way for it?
Just implement the equals method in the class Klass1.
public class Klass1 {
String bir;
String myID;
Klass1(String bir, String myID)
{
this.bir=bir;
this.myID=myID;
}
public boolean equals(Object o){
if(o instanceof Klass1)
return ((Klass1)o).myID.equals(myID);
else
return false;
}
}
Its because you are trying to delete a new object which isnt in the arraylist. When you use new klass1("vz","45") you are creating a new instance of this class which isnt in the arraylist.
What the system does internally is to compare those classes using equals. Why this doesn't work is explained in the following code:
Object o1 = new Object();
Object o2 = new Object();
System.out.println(o1 == o2); // false, obviously
System.out.println(o1.equals(o2)); // false
System.out.println(o1); // java.lang.Object#17046822
System.out.println(o2); // java.lang.Object#22509bfc
You can tell by the number following the # that these objects have a different hash values, and this is what the equals function of Object does check.
This is relevant for your klass, because unless you overwrite equals, you will use the equals of Object. And if you implement equals you should always implement hashcode as well. Because both tell you something about whether or not two objects are the "same", and if the one says something else than the other, some part of your code might get confused.
How to properly implement equals for your class:
#Override
public int hashCode() {
int hash = 7;
hash = 17 * hash + Objects.hashCode(this.bir);
hash = 17 * hash + Objects.hashCode(this.myID);
return hash;
}
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final klass1 other = (klass1) obj;
if (!Objects.equals(this.bir, other.bir)) {
return false;
}
if (!Objects.equals(this.myID, other.myID)) {
return false;
}
return true;
}
This can be done in most IDEs btw with a shortcut (i.E. alt-insert in Netbeans). Note that I did this in Java 7 using Objects. If you are in Java 6, you need to manually type(a == b) || (a != null && a.equals(b)); with the appropriate objects to compare.
Creating a proper hashcode is not always trivial, for more complex objects you might want to read a bit about hashcodes first. For simple objects: multiply primes with something.
The equals method is usually trivial, it is just important to first check for null and for class equality. This is often forgotten by programmers and a common source for NullPointerExceptions and ClassCastExceptions.

Categories