HashSet: Cannot Remove An Object That Is Equal? - java

In my code, I am trying to remove an element in a HashSet. I have overridden the equals method in my object.
for (BitVector bv : implicantsToDeleteViaBitVector) {
System.out.println(bv + " , " + bv.hashCode());
for(BitVector bvSetbv : bvSet){
if(bv.equals(bvSetbv)){
System.out.println("match " + bv);
}else{
System.out.println("No match " + bv + "," + bvSetbv);
}
}
if(bvSet.remove(bv)){
System.out.println("Remove");
}else{
System.out.println("No match found by remove");
}
...
The problem is that my equals method will return true but my remove returns false on the same item being examined. This is the equals method:
#Override
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof BitVector))
return false;
BitVector v = (BitVector) o;
boolean sameSize = mySize == v.getSize();
boolean sameTerms = this.bitVectorList
.containsAll(v.getBitVectorList());
return (sameSize && sameTerms);
}
Does remove use the equals() method in a class? If so, then why is this code not working?

You state:
I have overridden the equals method in my object.
You need to also override the hashCode() method, and its contract should be that if two objects are equal, then their hashCodes also must be equal, although the converse is not necessarily true in that it is possible and allowed for two objects to have the hash code and not be equal, although this is something that should be avoided if possible.

One of the possible problems is a mutable object (more accurately mutable hashcode). When you add an object to HashSet hashcode will be calculated only once. So if you change the object and try to 'remove' the SAME object from the collection the object will not be deleted.

By overriding hasCode and equal method following way, you can fix your problem
public int hashCode(){
System.out.println("In hashcode");
int hashcode = 0;
hashcode = price*20;
hashcode += item.hashCode();
return hashcode;
}
public boolean equals(Object obj){
System.out.println("In equals");
if (obj instanceof Price) {
Price pp = (Price) obj;
return (pp.item.equals(this.item) && pp.price == this.price);
} else {
return false;
}
}
public String toString(){
return "item: "+item+" price: "+price;
}

Related

Comparing two objects names in an equals method

I'm a little lost on how to correctly compare objects that I'm testing. My issue is that the tests themselves always come out as true due to the code, but any other way I think of doesn't work correctly either.
public class Element {
private String atomLetter;
private String name;
public Element(String atomLetter, String name) {
this.atomLetter = atomLetter.toUpperCase();
this.name = name.toLowerCase();
}
public Element(String atomLetter) {
this(atomLetter, "");
}
public String getAtomLetter() {
return atomLetter;
}
public String getName() {
return name;
}
// TODO: two elements are considered to be equal if they have the same atom letter.
#Override
public boolean equals(final Object obj) {
if (atomLetter == this.atomLetter){
return true;
}
return false;
}
#Override
public String toString() {
return "Element{" +
"'" + atomLetter + "'" +
", name='" + name + '\'' +
'}';
}
}
In this case, the outcome comes out exactly the same, but the issue is the equals method.
#Test
public void testSimpleMolecules() {
// simple carbon
Molecule m1 = new Molecule("");
assertTrue(m1.isEmpty());
assertEquals(0, m1.size());
m1.add(new Element("C"));
assertFalse(m1.isEmpty());
assertEquals(1, m1.size());
assertEquals(new Element("C"), m1.get(0));
// simple hydrogen
Molecule m2 = new Molecule("");
m2.add(new Element("H"));
assertFalse(m2.isEmpty());
assertEquals(1, m2.size());
assertEquals(new Element("H"), m2.get(0));
// simple nitrogen
Molecule m3 = new Molecule("");
m3.add(new Element("N"));
assertFalse(m3.isEmpty());
assertEquals(1, m3.size());
assertEquals(new Element("N"), m3.get(0));
// simple oxygen
Molecule m4 = new Molecule("");
m4.add(new Element("O"));
assertFalse(m4.isEmpty());
assertEquals(1, m4.size());
assertEquals(new Element("O"), m4.get(0));
}
In your equals method, you are comparing this object's atomLetter to itself.
if (atomLetter == this.atomLetter){
Instead, you need to cast the obj argument to the Element class and compare its atomLetter to this.atomLetter
Element other = (Element) obj;
return this.atomLettet == other.atomLettet;
Of course, you'll likely want to test that the cast is possible before actually doing it, and say that the objects are not equal if they are of different classes. Also test for null. The javadoc for object.Equals() explains all if the requirements for a proper equals method.
The Answer by DarkSigma is correct about your comparison of this.atomLetter to itself. You have a few other issues with your equals.
Compare content, not references
Your code … == this.atomLetter is comparing object references (pointers) rather than the textual content of those String objects. In other words, you are asking if the two variables both refer to the same object, that is, the same chunk of memory.
Always compare String content by calling String::equals or String::equalsIgnoreCase.
For implementing equals, you can test for references being the same, as a quick first part of the equality testing. But this alone is not enough.
if ( this == other ) return true;
Test for null
You should test for null. If the other object reference is null, there is no object, so there can be no equality.
if ( other == null ) return false;
Test for class
You can also make sure the class of the two objects match.
if ( other == null || getClass() != other.getClass() ) return false;
Cast
As the other Answer mentioned, you should cast the passed Object, having gotten past the class-matching test shown above.
Element element = ( Element ) other;
Check for matching content
As the last test, check for matching content.
In this particular case, I suspect you do care about case matching. So we call String::equals rather than String::equalsIgnoreCase.
return getAtomLetter().equals( element.getAtomLetter() );
Example equals method
Let's pull that all together into a single equals implementation.
#Override
public boolean equals ( Object other )
{
if ( this == other ) return true;
if ( other == null || getClass() != other.getClass() ) return false;
Element element = ( Element ) other;
return getAtomLetter().equals( element.getAtomLetter() );
}
Tip: Your IDE will generate this code for you. No need to write this yourself. For example, in IntelliJ, choose: Code > Generate > equals() and hashCode.
Always implement hashCode when implementing equals
As discussed many times on Stack Overflow, such as here, when writing an equals method, always write a hashCode method using the same logic.
#Override
public int hashCode ( )
{
return Objects.hash( getAtomLetter() );
}
Example class
So we end up with a Element class that looks like this.
package work.basil.example;
import java.util.Objects;
public class Element
{
// Member fields
private String atomLetter, name;
// Constructor
public Element ( String atomLetter , String name )
{
this.atomLetter = Objects.requireNonNull( atomLetter ).toUpperCase();
if ( this.atomLetter.isBlank() ) { throw new IllegalArgumentException();}
this.name = Objects.requireNonNull( name ).toLowerCase();
}
// Getters (read-only).
public String getAtomLetter ( ) {return atomLetter;}
public String getName ( ) {return name;}
// `Object` overrides
#Override
public boolean equals ( Object other )
{
if ( this == other ) return true;
if ( other == null || getClass() != other.getClass() ) return false;
Element element = ( Element ) other;
return getAtomLetter().equals( element.getAtomLetter() );
}
#Override
public int hashCode ( )
{
return Objects.hash( getAtomLetter() );
}
#Override
public String toString ( )
{
return "Element{ " +
"atomLetter='" + atomLetter + '\'' +
" | name='" + name + '\'' +
" }";
}
}
You can implement the equals method this way. However, you also have to implement hashCode for correctness.
For equals
#Override
public boolean equals(Object obj)
{
// check the instance of obj
if (!(obj instanceof Element)) return false;
// check if obj is itself
if (obj == this) return true;
// cast obj as Element
Element e = (Element) obj;
// compare fields
return this.atomLetter.equals(e.atomLetter) &&
this.name.equals(e.name);
}
For hashcode, you can implement it a number of ways, but usually this is the quickest and easiest way.
#Override
public int hashCode()
{
return Objects.hash(atomLetter, name);
}

get() and contains() methods do not find object in HashSet although it can be found by iterating and using equals() and the hashcode is the same

I have a program which first generates a Hashmap with all allowed instances of a particular object, called BoardState, as the keys. I then iterate over the keyset, creating copies of the BoardState objects and performing transformations on them and then looking up the transformed objects in my statemap and updating their associated values. The problem is that when I use the contains() method on this keyset (either directly or by first creating a HashSet of the keys) it will sometimes return false for my new object even though the object does exist in the map.
I know that the obvious answer here is that there's something wrong with my implementation of either equals() or hashcode() in BoardState or one of its fields, and I would be inclined to agree. In fact I have been able to narrow the problem down somewhat. BoardState includes as an instance variable a HashSet of Box objects, which I also implemented, and setting the hashcode() method of Box to return a constant resolves the issue (though this obviously is not an acceptable solution).
The thing is, that when I am getting the error I can still iterate through my keyset and find the object by comparing using equals(). If I then output the hashcode for this object and the object I am checking against I get the same result for each, so I'm at a loss as to why it is that contains() is throwing an error.
I apologise if the below code is a bit meaty, I've tried to strip out what I can and only show what's relevant to the error.
public class BoardState {
private static int size;
private static int totalTokens;
private static HashMap<Colour, Integer> colours;
private static HashSet<Token> fullTokenSet;
private int inactiveBoxes;
private HashSet<Box> boxes;
private HashSet<Token> offBoardTokens;
public BoardState(...){...}
public boolean checkRemoveBox(final Box box,
final HashMap<BoardState, Boolean> stateMap) {
BoardState checkState = copy();
checkState.remove(box, box.getBoxColours());
if (!stateMap.keySet().contains(checkState)) {
for (BoardState state : stateMap.keySet()) {
if (state.equals(checkState)){
System.out.println("state hashcode: " + Objects.hash(state));
System.out.println("checkstate hashcode: " +
Objects.hash(checkState));
}
}
throw new IllegalStateException ("State not found.");
} else {
if (!stateMap.get(checkState)) {
return false;
}
}
return true;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BoardState state = (BoardState) o;
return size == state.size &&
inactiveBoxes == state.inactiveBoxes &&
totalTokens == state.totalTokens &&
boxes.equals(state.boxes) &&
fullTokenSet.equals(state.fullTokenSet) &&
offBoardTokens.equals(state.offBoardTokens) &&
colours.equals(state.colours);
}
#Override
public int hashCode() {
return Objects.hash(inactiveBoxes, boxes, offBoardTokens);
}
}
public class Box {
private static int totalTokens;
private HashSet<Token> tokens;
Box(...) {...}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Box box = (Box) o;
return totalTokens == box.totalTokens &&
Objects.equals(tokens, box.tokens);
}
#Override
public int hashCode() {
return tokens.hashCode();
}
}
The given code gives the following output:
state hashcode: 157760
checkstate hashcode: 157760
Exception in thread "main" java.lang.IllegalStateException: State not found.
at game.BoardState.checkSplitBox(BoardState.java:306)
at game.BoardState.checkSplit(BoardState.java:284)
at game.Game.checkForP1Win(Game.java:173)
at game.Main.main(Main.java:11)
Process finished with exit code 1

Why am I getting duplicate keys in Java HashMap? [duplicate]

This question already has answers here:
Java 1.7 Override of hashCode() not behaving as I would expect
(2 answers)
Closed 6 years ago.
I seem to be getting duplicate keys in the standard Java HashMap. By "duplicate", I mean the keys are equal by their equals() method. Here is the problematic code:
import java.util.Map;
import java.util.HashMap;
public class User {
private String userId;
public User(String userId) {
this.userId = userId;
}
public boolean equals(User other) {
return userId.equals(other.getUserId());
}
public int hashCode() {
return userId.hashCode();
}
public String toString() {
return userId;
}
public static void main(String[] args) {
User arvo1 = new User("Arvo-Part");
User arvo2 = new User("Arvo-Part");
Map<User,Integer> map = new HashMap<User,Integer>();
map.put(arvo1,1);
map.put(arvo2,2);
System.out.println("arvo1.equals(arvo2): " + arvo1.equals(arvo2));
System.out.println("map: " + map.toString());
System.out.println("arvo1 hash: " + arvo1.hashCode());
System.out.println("arvo2 hash: " + arvo2.hashCode());
System.out.println("map.get(arvo1): " + map.get(arvo1));
System.out.println("map.get(arvo2): " + map.get(arvo2));
System.out.println("map.get(arvo2): " + map.get(arvo2));
System.out.println("map.get(arvo1): " + map.get(arvo1));
}
}
And here is the resulting output:
arvo1.equals(arvo2): true
map: {Arvo-Part=1, Arvo-Part=2}
arvo1 hash: 164585782
arvo2 hash: 164585782
map.get(arvo1): 1
map.get(arvo2): 2
map.get(arvo2): 2
map.get(arvo1): 1
As you can see, the equals() method on the two User objects is returning true and their hash codes are the same, yet they each form a distinct key in map. Furthermore, map continues to distinguish between the two User keys in the last four get() calls.
This directly contradicts the documentation:
More formally, if this map contains a mapping from a key k to a value v such that (key==null ? k==null : key.equals(k)), then this method returns v; otherwise it returns null. (There can be at most one such mapping.)
Is this a bug? Am I missing something here? I'm running Java version 1.8.0_92, which I installed via Homebrew.
EDIT: This question has been marked as a duplicate of this other question, but I'll leave this question as is because it identifies a seeming inconsistency with equals(), whereas the other question assumes the error lies with hashCode(). Hopefully the presence of this question will make this issue more easily searchable.
The issue lies in your equals() method. The signature of Object.equals() is equals(OBJECT), but in your case it is equals(USER), so these are two completely different methods and the hashmap is calling the one with Object parameter. You can verify that by putting an #Override annotation over your equals - it will generate a compiler error.
The equals method should be:
#Override
public boolean equals(Object other) {
if(other instanceof User){
User user = (User) other;
return userId.equals(user.userId);
}
return false;
}
As a best practice you should always put #Override on the methods you override - it can save you a lot of trouble.
Your equals method does not override equals, and the types in the Map are erased at runtime, so the actual equals method called is equals(Object). Your equals should look more like this:
#Override
public boolean equals(Object other) {
if (!(other instanceof User))
return false;
User u = (User)other;
return userId.equals(u.userId);
}
OK, so first of all, the code doesn't compile. Missing this method:
other.getUserId()
But aside from that, you'll need to #Override equals method, IDE like Eclipse can also help generating equals and hashCode btw.
#Override
public boolean equals(Object obj)
{
if(this == obj)
return true;
if(obj == null)
return false;
if(getClass() != obj.getClass())
return false;
User other = (User) obj;
if(userId == null)
{
if(other.userId != null)
return false;
}
else if(!userId.equals(other.userId))
return false;
return true;
}
Like others answered you had a problem with the equals method signature. According to Java equals best practice you should implement equals like the following :
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return userId.equals(user.userId);
}
Same thing applies for the hashCode() method. see Overriding equals() and hashCode() method in Java
The Second Problem
you don't have duplicates anymore now, but you have a new problem, your HashMap contains only one element:
map: {Arvo-Part=2}
This is because both User objects are referencing the same String(JVM String Interning), and from the HashMap perspective your two objects are the same, since both objects are equivalent in hashcode and equals methods. so when you add your second object to the HashMap you override your first one.
to avoid this problem, make sure you use a unique ID for each User
A simple demonstration on your users :

Checking if a value is already in a List won't work

I am doing a small program that holds shelves in a library list. If the number of shelf was already entered before, you can't enter it again. However, it's not working.
Here is my code in the main class:
Shelf s = new Shelf(1);
Shelf s2 = new Shelf(1);
Library l = new Library();
l.Addshelf(s);
l.Addshelf(s2);
As you can see I entered 1 in both objects as the shelf number so this code below should then run from the library class
public void Addshelf(Shelf s)
{
List li = new ArrayList();
if(li.contains(s))
{
System.out.println("already exists");
} else {
li.add(s);
}
}
The problem must be in the above method. I want to know how I check if that shelf number already exists in the list, in which case it should prompt me with the above statement - "already exists.
You'll have to override equals method in Shelf in order to get the behavior you desire.
Without overriding equals, ArrayList::contains, which calls ArrayList::indexOf, would use the default implementation of Object::equals, which compares object references.
#Override
public boolean equals (Object anObject)
{
if (this == anObject)
return true;
if (anObject instanceof Shelf) {
Shelf anotherShelf = (Shelf) anObject;
return this.getShelfNumber() == anotherShelf.getShelfNumber(); // assuming this
// is a primitive
// (if not, use equals)
}
return false;
}
If you look at the Javadoc for List at the contains method you will see that it uses the equals()method to evaluate if two objects are the same. So you have to override the method equals on your Shelf class.
Example:
public class Shelf
{
public int a;
public Shelf (int x)
{
this.a= x;
}
#Override
public boolean equals(Object object)
{
boolean isEqual= false;
if (object != null && object instanceof Shelf)
{
isEqual = (this.a == ((Shelf) object).a);
}
return isEqual;
}
}
Make sure that you have override equals() method in Shelf.
From Java doc. How contains() works?
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)).
^^
Try overriding methods hashCode() and equals(Object obj) in your Shelf class and then call contains.
Equals and HashCode tutorial

Comparing two objects using an equals method, Java

I have an array of objects that I want to compare to a target object. I want to return the number of objects that exactly match the target object.
Here is my count method:
public int countMatchingGhosts(Ghost target) {
int count=0;
for (int i=0;i<ghosts.length;i++){
if (ghosts[i].equals(target));
count++;
}
return count;
And here is my equals method:
public boolean equals(Ghost other){
if(this == other) return true;
if( !(other instanceof Ghost) ) return false;
Ghost p = (Ghost)other;
if (this.x == p.x && this.y == p.y && this.direction==p.direction && this.color.equals(p.color))
return true;
else
return false;
I run some test code, and I expect 1 matching only, but I get 3 instead. Do you see any errors?
There is a ; at the end of your if:
if (ghosts[i].equals(target));
^
This makes count++; happen always irrespective of what your equals method returns.
You should override this function:
public boolean equals(Object other) { }
Do note the Object class being used in method's signature instead of Ghost. Your can use #Override annotation to get a compiler error if you are not using method signature correctly.
#Override
public boolean equals(Object other) { }
Having said that, what's probably happening in your code is what the other answer is stating...
Just thought I add that while implementing the equals method in your code, you must also implement (override) the hashCode method. This is a general contract that you must follow for the best performances.
Below is an excerpt from Joshua Bloch's book "Effective Java"
Item 9: Always override hashCode when you override equals
A common source of bugs is the failure to override the hashCode method. You
must override hashCode in every class that overrides equals. Failure to do so
will result in a violation of the general contract for Object.hashCode, which will
prevent your class from functioning properly in conjunction with all hash-based
collections, including HashMap,HashSet, and Hashtable.
Here is the contract, copied from the Object specification [JavaSE6]:
• Whenever it is invoked on the same object more than once during an execution
of an 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.
And just like Pablo said, if you use anything other than the Object class in your equals method signature, you aren't actually overriding the equals method, and your program won't work as expected.
Take for example this small program that copies a List to a Set(which cannot contain duplicates) and prints the new Collection. Try swapping equals(Object obj) with equals(Item obj) and see what happens when you run the program. Also, comment out the hashCode() method and run the program and observe the difference between using it and not.
public class Item {
private String name;
private double price;
private String countryOfProduction;
public Item(String name, double price, String countryOfProduction) {
this.setName(name);
this.setPrice(price);
this.setCountryOfProduction(countryOfProduction);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public String getCountryOfProduction() {
return countryOfProduction;
}
public void setCountryOfProduction(String countryOfProduction) {
this.countryOfProduction = countryOfProduction;
}
public String toString() {
return "Item Name: " + getName() + "\n" +
"Item Price: N" + getPrice() + "\n" +
"Country of Production: " + getCountryOfProduction() + "\n";
}
#Override
public boolean equals(Object obj) {
if(!(obj instanceof Item)) {
return false;
}
if(obj == this) {
return true;
}
Item other = (Item)obj;
if(this.getName().equals(other.getName())
&& this.getPrice() == other.getPrice()
&& this.getCountryOfProduction().equals(other.countryOfProduction)) {
return true;
} else {
return false;
}
}
public int hashCode() {
int hash = 3;
hash = 7 * hash + this.getName().hashCode();
hash = 7 * hash + this.getCountryOfProduction().hashCode();
hash = 7 * hash + Double.valueOf(this.getPrice()).hashCode();
return hash;
}
public static void main (String[]args) {
List<Item> items = new ArrayList<>();
items.add(new Item("Baseball bat", 45, "United States"));
items.add(new Item("BLUESEAL Vaseline", 1500, "South Africa"));
items.add(new Item("BLUESEAL Vaseline", 1500, "South Africa"));
Collection<Item> noDups = new HashSet<>(items);
noDups.stream()
.forEach(System.out::println);
}
}

Categories

Resources