ArrayList contains() is not using equals() method of a class - java

I know there are so many answers for this question. But I have not found the solution.
class IpAddressRange
{
InetAddress start;
InetAddress end;
public IpAddressRange(String start, String end) throws Exception
{
this.start = InetAddress.getByName(start);
this.end = InetAddress.getByName(end);
}
#Override
public boolean equals(Object input)
{
System.out.println("Inside equals");
long lv = IpAddressRange.ipToLong(start);
long hv = IpAddressRange.ipToLong(end);
if(input != null && input instanceof InetAddress)
{
long iv = IpAddressRange.ipToLong((InetAddress)input);
if( iv >= lv && iv <= hv)
return true;
}
return false;
}
#Override
public String toString()
{
return start.getHostAddress() + "-" + end.getHostAddress();
}
public static long ipToLong(InetAddress ip) {
byte[] octets = ip.getAddress();
long result = 0;
for (byte octet : octets) {
result <<= 8;
result |= octet & 0xff;
}
return result;
}
}
When I use contains() on the ArrayList, it is not using equals() method.
ArrayList<IpAddressRange> allocatedList = new ArrayList<IpAddressRange>();
allocatedList.add(new IpAddressRange("10.10.10.10","10.10.10.12"));
Below is the code that calls contains():
InetAddress inetAddress1 = InetAddress.getByName("10.10.10.11");
allocatedList.contains(inetAddress1);
But this contains() is not calling the equals() method of IpAdressRange class.

The problem is that your implementation for equals() does not agree with the implementation of InetAddress. The equals() method should be symmetric.
Take a look at the contract here:
The equals method implements an equivalence relation on non-null object references:
It is reflexive: for any non-null reference value x, x.equals(x)
should return true.
It is 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.
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.
It is consistent: for any
non-null reference values x and y, multiple invocations of x.equals(y)
consistently return true or consistently return false, provided no
information used in equals comparisons on the objects is modified. For
any non-null reference value x, x.equals(null) should return false.
The point is that you might be able to implement it like anIpAddressRange.equals(anInetAddress) returns true, but not the other way around, because you cannot edit the equals() method from InetAddress.

Related

Does a map using equals method for key checking exists?

I want to store data in a map, with key unicity, but I would like the map to use the equals method of my key class.
It seems that HashMap doesn't use the equals method (I may be wrong, if so my tests are wrong).
My problem here is that the map use hashCode to check for duplicate, and I would like a map implementation that use equals.
I am storing timestamp in the key, and would like to make it so that 2 keys are equals if there timestamp difference does not exceed a defined amount (let say 1000 ms).
Edit : code
public class CleanKey
{
private DateTime start;
private DateTime end;
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((end == null) ? 0 : end.hashCode());
result = prime * result + ((start == null) ? 0 : start.hashCode());
return result;
}
public boolean equals(Object obj)
{
if(this == obj)
return true;
if(obj == null)
return false;
if(getClass() != obj.getClass())
return false;
CleanKey other = (CleanKey) obj;
if(end == null)
{
if(other.end != null)
return false;
}
else if(Math.abs(Millis.millisBetween(end, other.end).getMillis()) > 1000)
return false;
if(start == null)
{
if(other.start != null)
return false;
}
else if(Math.abs(Millis.millisBetween(start, other.start).getMillis()) > 1000)
return false;
return true;
}
}
It seems that HashMap doesn't use the equals method (I may be wrong, if so my tests are wrong).
It does use equals, but it uses hashCode first. It will only bother calling equals on keys with the same hash code - that's how it manages to be efficient. That's not a problem so long as your hashCode and equals method obey the contract specified in java.lang.Object.
I am storing timestamp in the key, and would like to make it so that 2 keys are equals if there timestamp difference does not exceed a defined amount (let say 1000 ms).
You can't do that. It violates the contract of equals, because you can't have transitivity. Suppose we have three keys x, y, and z with the following timestamps:
x 400
y 1200
z 2000
By your description, x.equals(y) would be true, y.equals(z) would be true, but x.equals(z) would be false, thus violating the contract of Object.equals.
The equals method implements an equivalence relation on non-null object references:
It is reflexive: for any non-null reference value x, x.equals(x) should return true.
It is 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.
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.
It is consistent: for any non-null reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.
For any non-null reference value x, x.equals(null) should return false.
You need to override hashCode and equals in you class.
Here: Understanding the workings of equals and hashCode in a HashMap
Edit after seeing the code :
Hashcode is returning wrong value because you are using end field to calculate the hash... different end lead to different hash.
Just for a try... return a constant and the hashmap will work

When do we say Object1 .equals(Object2) is true?

I have below code written:
public class Test{
int a;
public static void main(String[] args){
Test t1 = new Test();
Test t2 = new Test();
if(!t1.equals(t2))
System.out.println("They're not equal");
if(t1 instanceof Test)
System.out.println("True");
}
}
And here is the Output:
They're not equal
True
I even tried to assign the same value to instance variable 'a' of both these objects, like below,
t1.a = 10;
t2.a = 10;
Still the same output.
May I know when the t1.equals(t2) will return True?
How does the equals() method work on objects?
By default, calling equals execute the equals method of Object class, which returns true only when you are comparing an instance to itself.
You can override this method in other classes, so that equals would return true when the properties of both objects are equal.
For example :
#Override
public boolean equals(Object other)
{
if (other == null)
return false;
if (other instance of Test) {
Test t = (test) other;
return this.a == t.a;
}
return false;
}
Adding this method to your Test class would change the result of t1.equals(t2) to true.
The Object.equals(Object) Javadoc says (in part),
Indicates whether some other object is "equal to" this one.
The equals method implements an equivalence relation on non-null object references:
It is reflexive: for any non-null reference value x, x.equals(x) should return true.
It is 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.
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.
It is consistent: for any non-null reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.
For any non-null reference value x, x.equals(null) should return false.
For your code you could override equals like
#Override
public boolean equals(Object o) {
if (o instanceof Test) {
return ((Test) o).a == a;
}
return false;
}
The default implementation of Object.equals treats two objects as equal only if they are exactly the same object, not just the same contents but the same reference.
Objects can have different implementations of equals, but you must program them explicitly: if you want to check that all fields are equal, you must actually write an equals implementation that checks that.

When does a.equals(a) return false?

I was wondering which are the cases where a variable in java could not be equal
(using the equals() method) to itself.
I am not talking object here but the variable itself
(as long as the code compiles and return false when calling equals).
The only situation in which it does that I found so far is:
public class A {
public static void main(String args[]){
A a = new A();
System.out.println(a.equals((a = null)));
}
}
Is there any other case in which a.equals(a) would return false?
EDIT: no overriding of equals() is allowed but you can Modify (cast, inherit) a as much as you want as long as the variable a compare itself in the end.
It could return false in multithreaded contexts, even with an equals implementation that fulfills the equals contract:
class Test {
public static final A a = new A();
public static void main(String... args) throws Exception {
new Thread() {
#Override
public void run() {
while (true) {
a.x += 1;
}
}
}.start();
Thread.sleep(10);
System.out.println(a.equals(a)); // <---
}
}
class A {
int x;
#Override
public boolean equals(Object o) {
return (o instanceof A) && ((A)o).x == x;
}
}
false
From the Object documentation of Oracle:
public boolean equals(Object obj)
Indicates whether some other object is "equal to" this one.
The equals method implements an equivalence relation on non-null object references:
It is 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.
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.
It is consistent: for any non-null reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.
For any non-null reference value x, x.equals(null) should return false.
The equals method for class Object implements the most discriminating possible equivalence relation on objects; that is, for any non-null reference values x and y, this method returns true if and only if x and y refer to the same object (x == y has the value true).
Note that it is generally necessary to override the hashCode method whenever this method is overridden, so as to maintain the general contract for the hashCode method, which states that equal objects must have equal hash codes.
Parameters:
obj - the reference object with which to compare.
Returns:
true if this object is the same as the obj argument; false otherwise.
So coming back to your question and analizing the documentation
It's false when a.equals(null); and when a and b (Objects of the classes A and B respectively) are compared, i.e. a.equals(b) will return false too.
In other cases it's true, because of:
It is reflexive: for any non-null reference value x, x.equals(x) should return true.
It clearly says that: not null reference to x (or a in this case):
a.equals(a); will be true
I support khale's and Frakcool's reply. In addition to that if you just need another case to get false try
System.out.println(a.equals((a = new A())));
The assignment essentially returns what is being assigned and that will equate to false if its not the calling object itself.
I don't think there is a way we can get this done, since calling equals to itself is always true. Let me explain what you're trying to achieve.
String foo = "";
bool a = foo.equals(foo); // Here true, the easy one
foo = "some value";
bool b = foo.equals(foo); // Here true, since it's changed and then compared to itself again
bool c = foo.equals(foo="some other value"); // Here should be true again, since the compiler takes first the arguments, makes the assignation, and then makes the equals method execution, so in compiler what happens is:
// 1. Make foo = "some other value"
// 2. Compares foo with foo
// Foo is already changed, so is equals to itself
I haven't tried myself, but that's what should happen.
If for some reason compiler breaks in line bool c = ... it's because equals does not receive String instantiation as a String parameter.
With a correctly implemented .equals(), a.equals(a) will never be false.
Passing an expression to the equals method:
a.equals(a = null);
is no more special than:
a.equals(b); or a.equals(null);
You're just comparing two different values, stuffing an expression into the equals calls doesn't change that.
A very interesting case is the one where you have a boxed Float, consider this code:
Float alpha = +0.0f;
Float beta = -0.0f;
boolean equal = alpha.equals(beta);
System.out.println("Equal: " + equal);
boolean equality = alpha.floatValue() == beta.floatValue();
System.out.println("Equality: " + equality);
This will print true for the first one and false for the second.
The opposite is true of the case of Float.NaN.

Evaluating objects containing Strings with Java ArrayList contains()

I would like to do a deeper String check of Objects to be able to do the following:
List<MyObj> myList = new ArrayList<MyObj>() {{
add(new MyObj("hello"));
add(new MyObj("world"));
}};
System.out.println(myList.contains("hello")); // true
System.out.println(myList.contains("foo")); // false
System.out.println(myList.contains("world")); // true
But getting false on each one with the following full code
/* Name of the class has to be "Main" only if the class is public. */
class Ideone {
public static class MyObj {
private String str;
private int hashCode;
public MyObj(String str) {
this.str = str;
}
public #Override boolean equals(Object obj) {
System.out.println("MyObj.equals(Object)");
if (obj == this) {
return true;
}
if (obj instanceof String) {
String strObj = (String) obj;
return strObj.equals(str);
}
return false;
}
public #Override int hashCode() {
if (hashCode == 0) {
hashCode = 7;
for (int i = 0; i < str.length(); ++i) {
hashCode = hashCode * 31 + str.charAt(i);
}
}
return hashCode;
}
}
public static final MyObj obj1 = new MyObj("hello");
public static final MyObj obj2 = new MyObj("world");
public static void main (String[] args) throws java.lang.Exception {
List<MyObj> myList = new ArrayList<MyObj>() {{
add(obj1);
add(obj2);
}};
System.out.println(myList.contains("hello"));
System.out.println(myList.contains("foo"));
System.out.println(myList.contains("world"));
}
}
If I'm right the List Object should use equals() and hashCode() to evaluate containing Objects.
So I #Override them and check their Strings additionally.
But it never gets into equals() as there's no output MyObj.equals(Object).
java.util.ArrayList#indexOf is used internally in ArrayList for contains().
There is a check,
o.equals(elementData[i])
So there is comparison of string with your object, so String.equals() is invoked for check of equality.
You are not fulfilling the equals contract at all:
The equals method implements an equivalence relation on non-null object references:
It is reflexive: for any non-null reference value x, x.equals(x) should return true. Yours is not reflexive.
It is 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. Yours is not symmetric.
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. Yours is not transitive
It is consistent: for any non-null reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.
For any non-null reference value x, x.equals(null) should return false.
So without respecting the contract for the method you can't expect intended behavior.
Just for example, what guarantees you that equals is going to be called on the object contained in the ArrayList and not in the other way, eg "hello".equals(new MyObj("hello")). You have no guarantees about it but since equals is normally supposed to be symmetric than you shouldn't mind.
As pointed out by others, the problem is that your equals method is never called. When you invoke myList.contains("hello"), ArrayList checks whether "hello".equals(<MyObj>), not the other way around.
Instead, I recommend implementing your equals method properly, so that two MyObject instances with equal value are considered equal, and then create a helper method like this:
public boolean myContains(List<MyObj> list, String value) {
return list.contains(new MyObj(value));
}
List<MyObj> myList = new ArrayList<MyObj>()
is a list of MyObj, so you need to use MyObj while checking myList.contains:
System.out.println(myList.contains(new MyObj("hello")));
System.out.println(myList.contains(new MyObj("foo")));
System.out.println(myList.contains(new MyObj("world")));
You're asking the List to compare a String to a MyObj... They are never going to be "equal". Try a map instead:
Map<String, MyObj> map = new HashMap<String, MyObj>() {{
put("hello", new MyObj("hello"));
put("world", new MyObj("world"));
}};
Then you can use:
if (map.containsKey("hello"))
and to get the corresponding object:
MyObj o = map.get("hello"); // get() returns null if key not found
It is not equals of your object called when contains executes but the one from String class.
And String implementation checks with instanceof whether the class is a String to perform String-like operations to determine the answer. If object is not a String it will return false;

HashSet contains duplicate entries

A HashSet only stores values ones, when the equals method says that they're the same. Thats what I thought.
But now i'm adding Elements to a HashSet where the equals method returns true and the size of the set still grows?? sorry I'm confused. Some hints where i'm wrong would be nice.
Element t1 = new Element(false, false, false, false);
Element t2 = new Element(true, true, true, true);
Element t3 = new Element(false, false, false, false);
if (t1.equals(t3))
System.out.println("they're equal");
Set<Element> set = new HashSet<>();
set.add(t1);
set.add(t2);
set.add(t3);
System.out.println("set size: " + set.size());
so in this example my console output is:
they're equal
set size: 3
That makes no sense to me.. shouldn the size be 2?
The problem is that your Element class has not overridden the equals and hashCode methods or these implementations are broken.
From Object#equals method javadoc:
The equals method implements an equivalence relation on non-null object references:
It is reflexive: for any non-null reference value x, x.equals(x) should return true.
It is 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.
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.
It is consistent: for any non-null reference values x and y, multiple invocations of -x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.
For any non-null reference value x, x.equals(null) should return false.
From Object#hashCode method javadoc:
The general contract of hashCode is:
Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.
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 hash tables.
Make sure the implementations of these methods satisfy these rules and your Set (backed by a HashSet) will work as expected.
Your objects have different hashes so HashSet "puts" then in different "buckets".
If you have your own model classes you need to change some basic functions work like done in the below example.
Execution code :
HashSet<MyModel> models = new HashSet<MyModel>();
for (int i = 1; i < 5; i++)
models.add(new MyModel(i + "", "Name :" + i + ""));
for (int i = 3; i < 5; i++)
models.add(new MyModel(i + "", "Name :" + i + ""));
for (Object object : models)
System.out.println(object);
Model Class :
/**
* Created by Arun
*/
public static class MyModel {
private String id = "";
private String name = "";
public MyModel(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
return getId();
}
#Override
public boolean equals(Object obj) {
return !super.equals(obj);
}
public int hashCode() {
return getId().hashCode();
}
}
Hope this helps.
Yes We Can implement it with the object of the classes which are not FINAL.
HashSet Checks for two methods hashCode() and equals() before adding any Object.
First it checks for the method hashCode(),if it returns the hashcode which is same with any of the object in Set, then it checks for the equals method for that object,which internally compares the references for both objects i.e this.obj1==obj.If these are the same references in that case it returns true means it is a duplicate value.
We can add duplicate non-final objects by overriding HashCode and equals method.
In HashCode() you can return same hashcode in case of same parameters.
See example:
public class Product {
int i;
Product(int a)
{
this.i=a;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + i;
return result;
}
#Override
public boolean equals(Object obj) {
/*if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Product other = (Product) obj;
if (i != other.i)
return false;
return true;*/
return true;
}
}
`
`
import java.util.HashSet;
import java.util.Set;
public class Main {
public static void main(String[] args) {
Product p1=new Product(1);
Product p2=new Product(1);
Product p3=new Product(1);
Set s=new HashSet();
s.add(p1);
s.add(p2);
s.add(p3);
System.out.println(s.size());
}
}
The output will be 1.
P.S:Without overriding these methods,output will be 3 since it will use their default behavior.

Categories

Resources