Reputation: 31
I'm having some troubles with getting on with my code, I'll give you an simple example (though it's going to be a little more complex, this simple code doesn't work properly either).
class Sign {
private String char;
private Integer freq;
public Sign(String c) {
this.char = c;
}
@Override
public boolean equals(Object o) {
String check = (String)o;
return check.equals(this.char);
}
@Override
public int hashCode() {
int hash = 7;
hash = 31 * hash + this.char.hashCode();
return hash;
}
}
I assume that there's always will be a String in equals method for simplicity reasons. There's some hashCode() also to make sure that the contains() method will work and here's the test itself:
ArrayList<Sign> queueOfSigns = new ArrayList<>();
Sign test = new Sign("C");
String c = "C";
queueOfSigns.add(test);
if(queueOfSigns.contains("C"))
System.out.println("I am here!");
No matter what, this simple test-code always returns false in that case - so "I'm here" message never appears. I've been trying some different ways of approach my code but it was because the idea of this is to get single characters from String text and check whether the single character is already present in the ArrayList. Nevertheless - without getting this simple test working properly I can't move on, so I would like to ask you - what am I missing. It's my first time actually with using equals() and hashCode() methods to get my own object working properly with contains() method.
Upvotes: 1
Views: 3642
Reputation: 270780
Your equals
method is implemented incorrectly. It violates the general contract of Object.equals
:
x.equals(x)
where x
is a Sign
will crash with an exception.x.equals(y)
does not return the same value as y.equals(x)
, if y
is a string and x
is a Sign
On a low level of abstraction, the cause of this problem is the implementation of contains
. As per the docs:
Returns true if this list contains the specified element. More formally, returns true if and only if this list contains at least one element e such that (o==null ? e==null : o.equals(e)).
ArrayList
actually calls o.equals(e)
with o
being the string you passed in. So it actually calls the equals
method in String
.
If contains
called e.equals(o)
, then your program would have printed "I'm here", but your equals
still violates the contract.
A better equals
implementation is something like this:
@Override
public boolean equals(Object o) {
if (o == null) {
return false;
}
if (o.getClass() == this.getClass()) {
Sign other = (Sign)o;
return other.$char.equals($char); // I have renamed 'char' to '$char' since the former is not a valid identifier
} else {
return false;
}
}
And your client code:
ArrayList<Sign> queueOfSigns = new ArrayList<>();
Sign test = new Sign("C");
Sign c = new Sign("C");
queueOfSigns.add(test);
if(queueOfSigns.contains(c))
System.out.println("I am here!");
EDIT:
I think this is what you are looking for:
arrayList.stream()
.filter(x -> x.getChar().equals("C"))
.findFirst().isPresent() // this returns true if a sign with C is found in the array list
Upvotes: 1
Reputation: 1073968
Your equals
implementation is incorrect. equals
has a specific contract; that code attempts to violate that contract. From the documentation:
The equals method implements an equivalence relation on non-null object references:
- It is reflexive: for any non-
null
reference valuex
,x.equals(x)
should returntrue
.- It is symmetric: for any non-
null
reference valuesx
andy
,x.equals(y)
should returntrue
if and only ify.equals(x)
returnstrue
.- It is transitive: for any non-
null
reference valuesx
,y
, andz
, ifx.equals(y)
returnstrue
andy.equals(z)
returnstrue
, thenx.equals(z)
should returntrue
.- It is consistent: for any non-
null
reference valuesx
andy
, multiple invocations ofx.equals(y)
consistently returntrue
or consistently returnfalse
, provided no information used inequals
comparisons on the objects is modified.- For any non-
null
reference valuex
,x.equals(null)
should returnfalse
.
There's no way to make an instance of your Sign
class equals
to a string.
Upvotes: 5