Cannot remove or find an object in Java's CopyOnWriteArraySet - java

I use CopyOnWriteArraySet to store one instance of a custom class, which looks like this:
public class MyClass{
String _name;
public MyClass(String name){
_name = name;
}
#Override
public int hashCode(){
return _name.hashCode();
}
#Override
public boolean equals(Object obj){
if (obj == this) return true;
if ((obj instanceof MyClass) == false) return false;
MyClass otherObject = (MyClass) obj;
return _name.equals(otherObject._name);
}
#Override
public String toString(){
return _name;
}
}
When I print the set, everything seems ok:
MyClass theObject = new MyClass("Object 1");
CopyOnWriteArraySet<MyClass> theSet = new CopyOnWriteArraySet();
theSet.add(theObject);
for (MyClass tmp : theSet){
System.out.println(tmp.toString());
}
The result is:
Object 1
So, obviously the object is in the set.
Now, I want to remove the object from the set:
theSet.remove(theObject);
Then I print the content of the set again.
The result:
Object 1
Very weird. So, I tried this:
System.out.println(String.valueOf(theSet.contains(theObject)));
The result:
false
Obviously, the set cannot find theObject although it's there.
So, I thought, there's something wrong with the equals() method.
Thus, I changed the method overrides of equals() and hashCode() by adding a console print to the first line of each function:
#Override
public int hashCode(){
System.out.println("hashCode() called");
return _name.hashCode();
}
#Override
public boolean equals(Object obj){
System.out.println("equals() called");
if (obj == this) return true;
if ((obj instanceof MyClass) == false) return false;
MyClass otherObject = (MyClass) obj;
return _name.equals(otherObject.name);
}
Then, I call again:
theSet.remove(theObject);
The result:
hashCode() called
So, the equals() method isn't called at all?
Can someone explain what's going on there?
I already tried to compare the hashCodes of theObject and the instance inside the set and they're both equal.

Strange..i have tested your codes. And it works well in my environment.
And the remove operation doesn't call hashCode() but call equals() instead.
The jdk what i used is 1.6.0_23.

HashSet's use the hashCode, however the CopyOnWriteArraySet is not a HashSet (neither is TreeSet) and neither call hashCode(). If hashCode is being called you are not using this collection.
It is very weird because I cannot reproduce your problem.
MyClass theObject = new MyClass("Object 1");
CopyOnWriteArrayList<MyClass> theSet = new CopyOnWriteArrayList();
// OR
CopyOnWriteArraySet<MyClass> theSet = new CopyOnWriteArraySet();
theSet.add(theObject);
System.out.println("After add.");
System.out.println(theSet);
theSet.remove(theObject);
System.out.println("\nAfter remove");
System.out.println(theSet);
prints
After add.
[Object 1]
After remove
[]
Even when I change hashCode to
public int hashCode() {
throw new UnsupportedOperationException();
}
it gets the same result because these classes don't use hashCode() (except in the hashCode() method)

I found the reason for the problem.
I'm using Hibernate which creates an own instance of org.hibernate.collection.PersistentSet which replaced my CopyOnWriteArraySet!
The fact that .contains() and .remove() didn't work was a bug in Hibernate: http://opensource.atlassian.com/projects/hibernate/browse/HHH-3799
The solution in my case was to not override the .hashCode() method.
Note: This might not be the best solution for all cases. For me, it worked though.
In the link above, there are several workarounds described.

Related

ArrayList.containsAll does not use my custom equals function

The following code is a JUnit test function which fails upon execution.
List<KGramPostingsEntry> a = new ArrayList<KGramPostingsEntry>();
List<KGramPostingsEntry> b = new ArrayList<KGramPostingsEntry>();
KGramPostingsEntry entry = new KGramPostingsEntry(1);
a.add(entry);
entry = new KGramPostingsEntry(1);
b.add(entry);
assertTrue(a.containsAll(b));
It uses the KGramPostingsEntry class:
package ir;
public class KGramPostingsEntry {
int tokenID;
public KGramPostingsEntry(int tokenID) {
this.tokenID = tokenID;
}
public KGramPostingsEntry(KGramPostingsEntry other) {
this.tokenID = other.tokenID;
}
public String toString() {
return tokenID + "";
}
public boolean equals(KGramPostingsEntry other) {
if(other.tokenID == this.tokenID) {
return true;
}
return false;
}
}
As you can see, there is an equals() function in the class that compares the tokenID of the different KGramPostingsEntry objects. It seems to me that this function is not used when calling containsAll() in the test. Further experimentation seems to verify this to be true:
List<KGramPostingsEntry> a = new ArrayList<KGramPostingsEntry>();
List<KGramPostingsEntry> b = new ArrayList<KGramPostingsEntry>();
KGramPostingsEntry entry = new KGramPostingsEntry(1);
a.add(entry);
b.add(entry);
assertTrue(a.containsAll(b));
Here, I'm inserting the same object in both lists. This test does not fail. As far as I've gathered, ArrayList makes a copy object of the object sent to add(), before storing a reference to that object. This means that the objects in the two Lists are not the same (even though they have the same tokenID), and that containsAll() does not check for object reference equality. But if it does not check for object reference equality and does not check the equals() function defined in my code, what does it check? The only plausible option to me is that it checks for object value equality, and that the two objects stored in the first test example are somehow different (even though their only property is tokenID, which is the same in both objects).
What is going on here? How can I make this test succeed the way I want it to?
Here the equals declaration of Object:
public boolean equals(Object obj)
(documentation). You're trying to override this method, but instead you overloaded it:
public boolean equals(KGramPostingsEntry other)
Notice how the argument type in your method is KGramPostingsEntry, which differs from the argument type in Object.equals, namely Object. When a method has the same name but different argument types, it is overloaded, not overridden.
When the ArrayList tries to compare its contents with equals, it'll use the most applicable overridden version of Object.equals. That unfortunately doesn't include your method.
Luckily the fix is easy: you need to implement your equals method with an Object argument:
public boolean equals(Object obj) {
if(obj == null || !(obj instanceof KGramPostingsEntry)) {
return false;
}
KGramPostingsEntry other = (KGramPostingsEntry) obj;
if(other.tokenID == this.tokenID) {
return true;
}
return false;
}
The equals method doesn't have the correct signature. It should be
public boolean equals(Object that) {
// ..
}

Java - Is it possible for equals() to return false, even if contents of two Objects are same?

I know this is the duplicate question but that question was not asked correctly so I did not the get the answer.
But I was being asked this question in one interview.
I want to know is it possible? If yes, can anyone provide me the code how?
Thanks in advance.
StringBuilder does this as it's mutable. The contents are not considered, only whether the objects are the same.
StringBuilder a = new StringBuilder();
StringBuilder b = new StringBuilder();
a.equals(b); // false as they are not the same object.
This is also true of all arrays which are objects
int[] a = {};
int[] b = {};
a.equals(b); // false, not the same object.
Arrays.equals(a, b); // true, contents are the same.
In java the method public boolean equals(Object obj) is inherited from the Object.class. Since all Java objects inherit (eventually) from Object, they all inherit that method as well. However, the implementation of the method as defined in the Object class is that the equals method will return if and only if the two objects being compared are the same instance.
public class WrappedString {
private final String str = "hello";
}
public void foo() {
WrappedString ws1 = new WrappedString();
WrappedString ws2 = new WrappedString();
System.out.println(ws1.equals(ws2));
}
The output of the above code snippet will be false since ws1 will only be equal to itself (e.g. other references to the same instance since equals is not overridden).
Yes, if you have a bad implementation of equals.
public boolean equals(Object o){
return false;
}
For instance, or, if they don't have the exact same type:
public boolean equals(Object o){
// o is an instance of a parent class, with exactly the same content. bad design, but possible.
if ( o == null ){
return false;
}
if ( !o.getClass().equals(this.getClass()){ // or a similar check
return false;
}
Child ot = (Child)o;
return this.content.equals(ot.getContent());
}
Yes. You can also override the equals() method and play with it.
class Person {
private String Name;
public Person(String name){
this.name = name;
}
#Override
public boolean equals(Object that){
if(this == that) return false; //return false if the same address
if(!(that instanceof People)) return true; //return true if not the same
People thatPeople = (People)that;
return !this.name.equals(thatPeople.name); //return false if the same name.
}
}

Which function should I override when using indexOf() function

Which function should I override when using the indexOf() function in java. I have a array list, then I take in an input as the ID and create a object which contains the ID and all the other elements are null, then I need to pass that object and get the index of the element which contains that object
The equals() method
public boolean equals(Object o) {
if (o instanceof MyObject) {
//id comparison
MyObject mo = (MyObject)o;
return mo.id.equals(id);
}
return false;
}
Change MyObject to your class.
Remember to change hashCode() as well as #Hovercraft points out. equals and hashCode go together (read the javadoc for them). Else you might run into some nasty and possibly hard to find bugs.
An example:
With java 7+ you can do this:
public int hashCode() {
return java.util.Objects.hashCode(id);
}

ArrayList not using the overridden equals

I'm having a problem with getting an ArrayList to correctly use an overriden equals. the problem is that I'm trying to use the equals to only test for a single key field, and using ArrayList.contains() to test for the existence of an object with the correct field. Here is an example
public class TestClass {
private static class InnerClass{
private final String testKey;
//data and such
InnerClass(String testKey, int dataStuff) {
this.testKey =testKey;
//etc
}
#Override
public boolean equals (Object in) {
System.out.println("reached here");
if(in == null) {
return false;
}else if( in instanceof String) {
String inString = (String) in;
return testKey == null ? false : testKey.equals(inString);
}else {
return false;
}
}
}
public static void main(String[] args) {
ArrayList<InnerClass> objectList = new ArrayList<InnerClass>();
//add some entries
objectList.add(new InnerClass("UNIQUE ID1", 42));
System.out.println( objectList.contains("UNIQUE ID1"));
}
}
What worries me is that not only am I getting false on the output, but I'm also not getting the "reached here" output.
Does anyone have any ideas why this override is being completely ignored? Is there some subtlety with overrides and inner classes I don't know of?
Edit:
Having problems with the site so I cant seem to mark the answered.
Thanks for the quick response: yes an oversight on my part that it is the String .equals thta is called, not my custom one. I guess it's old fashioned checks for now
If you check sources of ArrayList, you will see that it calls equals of other object. In your case it will call equals of String "UNIQUE ID1" which will check that other object is not of type String and just returns false:
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
public int indexOf(Object o) {
...
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
...
return -1;
}
For your case call contains with InnerClass that only contains id:
objectList.contains(new InnerClass("UNIQUE ID1"))
Don't forget to implement equals for InnerClass which compares id only.
According to the JavaDoc of List.contains(o), it is defined to return true
if and only if this list contains at least one element e such that (o==null ? e==null : o.equals(e)).
Note that this definition calls equals on o, which is the parameter and not the element that is in the List.
Therefore String.equals() will be called and not InnerClass.equals().
Also note that the contract for Object.equals() states that
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.
But you violate this constraint, since new TestClass("foo", 1).equals("foo") returns true but "foo".equals(new TestClass("foo", 1)) will always return false.
Unfortunately this means that your use case (a custom class that can be equal to another standard class) can not be implemented in a completely conforming way.
If you still want to do something like this, you'll have to read the specification (and sometimes the implementation) of all your collection classes very carefully and check for pitfalls such as this.
You're invoking contains with an argument that's a String and not an InnerClass:
System.out.println( objectList.contains("UNIQUE ID1"))
In my JDK:
public class ArrayList {
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
public int indexOf(Object o) {
if (o == null) {
// omitted for brevity - aix
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i])) // <<<<<<<<<<<<<<<<<<<<<<
return i;
}
return -1;
}
}
Note how indexOf calls o.equals(). In your case, o is a String, so your objectList.contains will be using String.equals and not InnerClass.equals.
Generally, you need to also override hashCode() but this is not the main problem here. You are having an asymmetric equals(..) method. The docs make it clear that it should be symmetric:
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.
And what you observe is an unexpected behaviour due to broken contract.
Create an utility method that iterates all items and verifies with equals(..) on the string:
public static boolean containsString(List<InnerClass> items, String str) {
for (InnerClass item : items) {
if (item.getTestKey().equals(str)) {
return true;
}
}
return false;
}
You can do a similar thing with guava's Iterables.any(..) method:
final String str = "Foo";
boolean contains = Iterables.any(items, new Predicate<InnerClass>() {
#Override
public boolean apply(InnerClass input){
return input.getTestKey().equals(str);
}
}
Your equals implementation is wrong. Your in parameter should not be a String. It should be an InnerClass.
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof InnerClass) return false;
InnerClass that = (InnerClass)o;
// check for null keys if you need to
return this.testKey.equals(that.testKey);
}
(Note that instanceof null returns false, so you don't need to check for null first).
You would then test for existence of an equivalent object in your list using:
objectList.contains(new InnerClass("UNIQUE ID1"));
But if you really want to check for InnerClass by String key, why not use Map<String,InnerClass> instead?
Although not answering your question, many Collections use hashcode(). You should override that too to "agree" with equals().
Actually, you should always implement both equals and hashcode together, and they should always be consistent with each other. As the javadoc for Object.equals() states:
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.
Specifically, many Collections rely on this contract being upheld - behaviour is undefined otherwise.
There are a few issues with your code. My suggestion would be to avoid overriding the equals entirely if you are not familiar with it and extend it into a new implementation like so...
class MyCustomArrayList extends ArrayList<InnerClass>{
public boolean containsString(String value){
for(InnerClass item : this){
if (item.getString().equals(value){
return true;
}
}
return false;
}
}
Then you can do something like
List myList = new MyCustomArrayList()
myList.containsString("some string");
I suggest this because if you override the equals should also override the hashCode and it seems you are lacking a little knowledge in this area - so i would just avoid it.
Also, the contains method calls the equals method which is why you are seeing the "reached here". Again if you don't understand the call flow i would just avoid it.
in the other way, your equal method gets called if you change your code as follows. hope this clears the concept.
package com.test;
import java.util.ArrayList;
import java.util.List;
public class TestClass {
private static class InnerClass{
private final String testKey;
//data and such
InnerClass(String testKey, int dataStuff) {
this.testKey =testKey;
//etc
}
#Override
public boolean equals (Object in1) {
System.out.println("reached here");
if(in1 == null) {
return false;
}else if( in1 instanceof InnerClass) {
return ((InnerClass) this).testKey == null ? false : ((InnerClass) this).testKey.equals(((InnerClass) in1).testKey);
}else {
return false;
}
}
}
public static void main(String[] args) {
ArrayList<InnerClass> objectList = new ArrayList<InnerClass>();
InnerClass in1 = new InnerClass("UNIQUE ID1", 42);
InnerClass in2 = new InnerClass("UNIQUE ID1", 42);
//add some entries
objectList.add(in1);
System.out.println( objectList.contains(in2));
}
}
As many posts have said, the problem is that list.indexOf(obj) function calls "equals" of the obj, not the items on the list.
I had the same problem and "contains()" didn't satisfy me, as I need to know where is the element!. My aproach is to create an empty element with just the parameter to compare, and then call indexOf.
Implement a function like this,
public static InnerClass empty(String testKey) {
InnerClass in = new InnerClass();
in.testKey =testKey;
return in;
}
And then, call indexOf like this:
ind position = list.indexOf(InnerClass.empty(key));
There are two errors in your code.
First:
The "contains" method called on "objectList" object should pass a new InnerClass object as the parameter.
Second:
The equals method (should accept the parameter as Object, and is correct) should handle the code properly according to the received object.
Like this:
#Override
public boolean equals (Object in) {
System.out.println("reached here");
if(in == null) {
return false;
}else if( in instanceof InnerClass) {
String inString = ((InnerClass)in).testKey;
return testKey == null ? false : testKey.equals(inString);
}else {
return false;
}
}
This post was first written before Java 8 was available but now that it's 2017 instead of using the List.containts(...) method you can use the new Java 8 way like this:
System.out.println(objectList.stream().filter(obj -> obj.getTestKey().equals("UNIQUE ID1")).findAny().isPresent());
And give your TestClass a getter for your testKey field:
public String getTestKey() {
return testKey;
}
The benefit of this approach is that you don't have to modify the equals or hash method and you'll look like a boss to your peers!

Java hashcode based on identity

The default behavior of Object.hashCode() is to return essentially the "address" of the object so that a.hashCode() == b.hashCode() if and only if a == b. How can I get this behavior in a user-defined class if a superclass already defines hashCode()? For instance:
class A {
public int hashCode() {
return 0;
}
}
class B extends A {
public int hashCode() {
// Now I want to return a unique hashcode for each object.
// In pythonic terms, it'd look something like:
return Object.hashCode(this);
}
}
Ideas?
System.identityHashCode(Object) provides this behaviour.
You would write this:
class B extends A {
public int hashCode() {
return System.identityHashCode(this);
}
}
Please check the equals-method, that it only returns true, if the two objects are the same. Otherwise it would break behaviour described for equals and hashCode. (To be correct, the equals-method has to return false, if you get different hashcodes for two objects.) To provide an implementation of equals() that comply with the given hashCode()-method:
public boolean equals(Object other){
return this == other;
}
Use System.identityHashCode(). This is what IdentityHashMap uses.
You should be extremely wary of overriding an existing hashCode() with this though because you might break the hashCode contract, being that two objects that:
if a.equals(b) then a.hashCode() must equal b.hashCode()
You might break this by overriding the existing behaviour or you might need to override equals() too.
As Mnementh said it all, I'd just like to point out that hashCode() returning 0 (or any constant value) is valid (while lame). hashCode() can (and should) return different values for a and b only if !a.equals(b).
So for example you have
class A {
public int hashCode() {
return 0;
}
public boolean equals(Object o) {
return o instanceof A; // all objects are equal
}
}
class B extends A {
public int hashCode() {
return System.identityHashCode(this);
}
public boolean equals(Object o) {
return this.hashCode().equals(o.hashCode());
}
}
Now you create two objects:
A a = new A();
A b = new B();
And suddenly a.equals(b), but !b.equals(a). Of course in more real life the equals() in A will be more sophisticated, but the problem still persist. To get rid of this problem you want to always call
if (super.equals(o)) return true;
at the beginning of new equals().
And since overriding hashCode() is strictly tied to overriding equals(), you want to make sure that everywhere super.equals() returned true for any two given objects, new hashCode() will return super.hashCode().

Categories