问题
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.
回答1:
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-
nullreference valuex,x.equals(x)should returntrue.- It is symmetric: for any non-
nullreference valuesxandy,x.equals(y)should returntrueif and only ify.equals(x)returnstrue.- It is transitive: for any non-
nullreference valuesx,y, andz, ifx.equals(y)returnstrueandy.equals(z)returnstrue, thenx.equals(z)should returntrue.- It is consistent: for any non-
nullreference valuesxandy, multiple invocations ofx.equals(y)consistently returntrueor consistently returnfalse, provided no information used inequalscomparisons on the objects is modified.- For any non-
nullreference valuex,x.equals(null)should returnfalse.
There's no way to make an instance of your Sign class equals to a string.
回答2:
Your equals method is implemented incorrectly. It violates the general contract of Object.equals:
- It is not reflexive - since it throws an exception when the argument is not a string,
x.equals(x)wherexis aSignwill crash with an exception. - It is not symmetric -
x.equals(y)does not return the same value asy.equals(x), ifyis a string andxis aSign - It is not consistent - since it can possibly throw exceptions when the argument is not a string, not just return true or false.
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
来源:https://stackoverflow.com/questions/52317618/arraylists-contains-method-always-returns-false-with-custom-object