HashSet does not realize that two objects are the same - java

I have looked at the other times this question has been asked on StackOverflow, but none of the other use cases seem to solve my problem. HashSet does not seem to realize that two objects are the same.
Basically, this is my class.
private static class Inconsistency
{
int a;
int b;
boolean isConsistency;
//Default constructor. To be used when we don't have an inconsistency
public Inconsistency()
{
this.a = -1;
this.b = -1;
this.isConsistency = false;
}
public Inconsistency(int a, int b, boolean isConsistency)
{
this.a = a;
this.b = b;
this.isConsistency = isConsistency;
}
#Override
public String toString()
{
if (this.isConsistency)
{
return "(" + this.a + ", " + this.b + ")";
}
else
{
return "No inconsistency";
}
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + a;
result = prime * result + b;
result = prime * result + (isConsistency ? 1231 : 1237);
return result;
}
#Override
public boolean equals(Object other)
{
if (this == other)
{
return true;
}
if (other == null)
{
return false;
}
if (this.getClass() != other.getClass())
{
return false;
}
Inconsistency otherInconsistency = (Inconsistency) other;
return ((this.a == otherInconsistency.a) && (this.b == otherInconsistency.b) && (this.isConsistency == otherInconsistency.isConsistency))
|| ((this.a == otherInconsistency.b) && (this.b == otherInconsistency.a) && (this.isConsistency == otherInconsistency.isConsistency));
}
}
I'm trying to store objects of my class inside a hashmap.
With the way I'm defining my equals method, an Inconsistency A (10, 20, true), should be equal to another Inconsistency B (20, 10, true) and when I test my equals method, this works correctly. However, when I try to insert both A and B into a HashSet, they both erroneously get added. I understand that I should manipulate my hashcode function but I'm not sure how to go about it.
Here's a driver showcasing the erroneous behaviour
Inconsistency A = new Inconsistency(10,20, true);
Inconsistency B = new Inconsistency(20,10, true);
System.out.println(A.equals(B)); // prints true as expected
HashSet<Inconsistency> test = new HashSet<>();
test.add(A);
test.add(B);
System.out.println(test); // prints [(10, 20), (20, 10)]. The two objects are equal but are both added to hashset
Just so the question is clear, How do I go about making sure that the two equal objects A and B both won't be added to my HashSet?

Your definition of equals means that two Inconsistency objects with their elements reversed are .equals, but your definition of hashCode does not return the same hash code if a and b are in a different order, which is a requirement if a HashSet or other hash-based collection is to work properly.
The simplest approach to fixing this is to do something commutative -- something that has the same result whichever order a and b are in. For example:
result = prime * result + a + b;
instead of
result = prime * result + a;
result = prime * result + b;

Related

HashSet contains method with custom class [duplicate]

I am writing a simple program as follow: Given two numbers M and N, p is from [M,N] and q is from [1,p-1], find all irreducible fractions of p/q.
My idea is brute force all possible value of p, q. And using HashSet to avoid duplicated fraction. However, somehow the contains function not working as expected.
My code
import java.util.HashSet;
import java.util.Set;
public class Fraction {
private int p;
private int q;
Fraction(int p, int q) {
this.p = p;
this.q = q;
}
public static int getGCD(int a, int b) {
if (b == 0)
return a;
else
return getGCD(b, a % b);
}
public static Fraction reduce(Fraction f) {
int c = getGCD(f.p, f.q);
return new Fraction(f.p / c, f.q / c);
}
public static HashSet<Fraction> getAll(int m, int n) {
HashSet<Fraction> res = new HashSet<Fraction>();
for (int p = m; p <= n; p++)
for (int q = 1; q < p; q++) {
Fraction f = new Fraction(p,q);
Fraction fr = reduce(f);
if (!res.contains(fr))
res.add(fr);
}
return res;
}
public static void print(Fraction f) {
System.out.println(f.p + "/" + f.q);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
HashSet<Fraction> res = getAll(2, 4);
for (Fraction f : res)
print(f);
}
}
Here is the output of program
4/3
3/1
4/1
2/1
3/2
2/1
you can see the fraction 2/1 is duplicated. Anyone can help me figure out why and how to fix it.
Many thanks.
Override the Object#equals and Object#hashCode methods in the Fraction class. These methods are used by HashSet to determine if two objects are the same. When you don't override them, the equals method tests equality of the objects' references rather that equality of their field values.
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + p;
result = prime * result + q;
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Fraction other = (Fraction) obj;
if (p != other.p)
return false;
if (q != other.q)
return false;
return true;
}
You need to implement Fraction#equals() and Fraction#hashcode(), because that is used for determining weather the set contains certain value or not. Without it, object references are compared, which will not give you the desired result.
Your Fraction class does not override hashCode and equals. A HashMap contains tries to find a key with the same hashCode (and equals) as the one you provided. As you create a new instance of Fraction, it will never be the same as the one already in the HashMap. Here is how you would do hashCode and equals:
#Override
public int hashCode() {
return super.hashCode() + p * 24 + q * 24;
}
#Override
public boolean equals(Object other) {
if (!(other instanceof Fraction)) return false;
return ((Fraction) other).p == this.p && ((Fraction) other).q == this.q;
}

Java HashSet contains returns false, even with overridden equals() and hashCode()

I initialize the HashSet like this:
private HashSet<Rule> ruleTable = new HashSet<Rule>();
The equals() and hashCode() methods of my TcpRule object (sub-class of abstract class Rule) look like this:
#Override
public int hashCode() {
// Ignore source Port for now
return (this.getSrcPool() + ":" + this.getDstPool() + ":" + this.getProtocol() + ":" + this.dstTcp).hashCode();
}
#Override
public boolean equals(Object obj) {
if (!(obj instanceof TcpRule))
return false;
if (obj == this)
return true;
TcpRule r = (TcpRule) obj;
return (this.getSrcPool().equals(r.getSrcPool()) && this.getDstPool().equals(r.getDstPool()) && this.getProtocol().equals(r.getProtocol()) && this.getSrcTcp() == r.getSrcTcp() && this.getDstTcp() == r.getDstTcp());
}
I have even written a simple unit test, which does not give any error:
#Test
public void equalsTest() {
Pool srcPool = new Pool("PROXY");
Pool dstPool = new Pool("WEB");
int srcTcp = 54321;
int dstTcp = 80;
TcpRule r1 = new TcpRule(srcPool, dstPool, srcTcp, dstTcp);
TcpRule r2 = r1;
assert r1.equals(r2);
TcpRule r3 = new TcpRule(srcPool, dstPool, srcTcp, dstTcp);
TcpRule r4 = new TcpRule(srcPool, dstPool, srcTcp, dstTcp);
assert r3.equals(r4);
}
#Test
public void hashCodeTest() {
Pool srcPool = new Pool("PROXY");
Pool dstPool = new Pool("WEB");
int srcTcp = 54321;
int dstTcp = 80;
TcpRule r1 = new TcpRule(srcPool, dstPool, srcTcp, dstTcp);
TcpRule r2 = new TcpRule(srcPool, dstPool, srcTcp, dstTcp);
assert r1.hashCode() == r2.hashCode();
HashSet<Rule> rules = new HashSet<Rule>();
rules.add(r1);
assert rules.contains(r1);
assert rules.contains(r2);
}
In my application, I have an add() method where I simply add a Rule object to the HashSet:
#Override
public void add(Rule rule) {
ruleTable.add(rule);
}
In another method, I check if a rule exists in the HashSet:
#Override
public boolean isPermittedTcp(IpAddress sourceAddress, IpAddress destinationAddress, short srcTcp, short dstTcp) {
Pool sourcePool = poolService.getPool(new Host(sourceAddress));
Pool destinationPool = poolService.getPool(new Host(destinationAddress));
Rule r = new TcpRule(sourcePool, destinationPool, srcTcp, dstTcp);
log.info("Checking: " + r.toString());
log.info("Hash-Code: " + r.hashCode());
log.info("Hashes in ruleTable:");
for(Rule rT : ruleTable) {
log.info("" + rT.hashCode());
}
if(ruleTable.contains(r)) {
log.info("Hash found!");
} else {
log.info("Hash not found!");
}
return ruleTable.contains(r);
}
The log messages indicate that the hash of the Rule object (r.hashCode()) is -1313430269, and that one hash in the HashSet (rT.hashCode() in the loop) is also -1313430269.
But ruleTable.contains(r) always returns false. What am I doing wrong?
I have found similar questions on StackOverflow, but these mostly involve the equals() or hashCode() methods not being (correctly) overridden. I think I have implemented this two methods correctly.
Your problem is that hashCode() and equals() do not agree.
Your hashCode() implementation is based on the toString() of the pool, but your equals() uses .equals() of the pool class.
Change your .equals() to compare the strings used to generate the hash code.
There are some possibilities:
Rule is mutable, after adding a rule to the set some key (w.r.t. hash or equals) field was changed;
If two objects are equal they should have the same hashCode;
Bug, like a comparison in equals using == i.o. equals.
Here I would guess you have two Pool instances without equals on pool name or hashCode on pool name.
You have an extra condition in equals this.getSrcTcp() == r.getSrcTcp() which is not part of hash code - maybe thats the issue, hashcode is same, but equals is false. Check if this field is different in the values you are comparing.
Inspite of comments, I think the reason this does not work is because the equals & hashCode implementations do not use the same fields.
Code to simulate the problem:
import java.util.HashSet;
/**
* #author u332046
*
*/
public class HashCodeCollisionTest {
public static class KeyDemo {
String id;
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
/*if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
KeyDemo other = (KeyDemo) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
return true;*/
return false;
}
public KeyDemo(String id) {
super();
this.id = id;
}
}
static HashSet<KeyDemo> set = new HashSet<>();
public static void main(String[] args) {
set.add(new KeyDemo("hi"));
set.add(new KeyDemo("hello"));
System.out.println(set.contains(new KeyDemo("hi")));
}
}
This prints false. Uncomment the equals code and it prints true

Remove duplicate in ArrayList of Custom objects

i'm trying to remove duplicate objects from my array.
I have my custom which is made of two double : x and y.
What i want to do is to remove duplicate ( (x && y) == (x1 && y1)) and if x == x1 i want to keep the object which has the higher y.
ArrayList<MyObject> list = [x(0),y(0)], [x(0),y(0)], [x(0.5),y(0.5], [x(0.5),y(0.6)], [x(1),y(1)];
ArrayList<MyObject> results = [x(0),y(0)], [x(0.5),y(0.6)], [x(1),y(1)];
I tried to implement the equals method but i do not how to use it :
public boolean equals(Object obj) {
if (obj == null || !(obj instanceof MyObject)) {
return false;
}
return (this.x == ((MyObject)obj).x);
}
list is always ordered using Collections.sort by x.
Thanks for all.
Given MyObject like this:
class MyObject {
private final double x;
private final double y;
public MyObject(double x, double y) {
this.x = x;
this.y = y;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MyObject myObject = (MyObject) o;
if (Double.compare(myObject.x, x) != 0) return false;
if (Double.compare(myObject.y, y) != 0) return false;
return true;
}
#Override
public int hashCode() {
int result;
long temp;
temp = Double.doubleToLongBits(x);
result = (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(y);
result = 31 * result + (int) (temp ^ (temp >>> 32));
return result;
}
}
You can implement a unique method that returns a list with unique elements only:
private List<MyObject> unique(List<MyObject> list) {
List<MyObject> uniqueList = new ArrayList<>();
Set<MyObject> uniqueSet = new HashSet<>();
for (MyObject obj : list) {
if (uniqueSet.add(obj)) {
uniqueList.add(obj);
}
}
return uniqueList;
}
And a unit test for it to verify it works:
#Test
public void removeDups() {
List<MyObject> list = Arrays.asList(new MyObject(0, 0), new MyObject(0, 0), new MyObject(0.5, 0.5), new MyObject(0.5, 0.6), new MyObject(1, 1));
List<MyObject> results = Arrays.asList(new MyObject(0, 0), new MyObject(0.5, 0.5), new MyObject(0.5, 0.6), new MyObject(1, 1));
assertEquals(results, unique(list));
}
Note: it's important to implement both equals and hashCode for this to work,
because of the use of a hash map. But you should always do this anyway in your custom classes: provide appropriate equals and hashCode implementations. Btw, I didn't write those equals and hashCode methods. I let my IDE (IntelliJ) generate them automatically from the fields x and y of the class.
Make sure to override equals() method in your custom Object(MyObject).
Then add them into a Set. Now you have unique result set.
Use a Set instead of a List ...
You have to override equals() and hashCode(). The most IDE can generate that for you!
To "convert" a List to a Set you can simply use this:
ArrayList<MyObject> list = ...
Set<MyObject> mySet = new HashSet<MyObject>(list);
Then you have a set with unique elements. You can iterate over the set like this:
for (MyObject o : mySet){
o.getX();
}
The most optimal solution would be if you could use a Set. However, there are two Set implementations in Java: HashSet and TreeSet. HashSet requires that you declare equals and hashCode methods, while TreeSet requires your class to implement Comparable with a compareTo method or supply a Comparator. Neither solution will work in your case because you want to keep the higher y when x is equal. If you sort/calculate equality based on x, y you will have duplicate x, and if you sort/calculate equality based on x only you will get the first x entered, which is not what you want.
Therefore, what we need to do is:
Sort by x ascending, y descending
Convert to Set that preserves original order, but bases equality only on x
Convert back to list (if necessary)
XAscYdesc Comparator Method (Not accounting for nulls):
public int compare(MyObject left, MyObject right) {
int c = left.x - right.x;
if(c != 0) {
return c;
}
return right.y - left.y;
}
XAsc Comparator Method (Not accounting for nulls):
public int compare(MyObject left, MyObject right) {
return left.x - right.x;
}
(Using the Guava library; it's very useful for one-liners like this):
Collections.sort(list, new XAscYdesc());
Lists.newArrayList(ImmutableSortedSet.copyOf(new XAsc(), list));
You need to order your collection on both x and y with x first (think about it as x and y forming an hypothetical number with x on the left side of the decimal point, and y on the right side: when you sort number in growing order, if the integral parts are equal, you sort on the decimal part).
Now, with an equal predicate, it will be difficult to sort values (you can only tell if they are equal, not if one is before another). Instead, you need to implement the comparable interface, with the following method:
public int compare(Object obj) {
if (obj == null || !(obj instanceof MyObject)) {
// raise an Exception
}
MyObject other = (MyObject)obj;
if (x < other.x) return -1;
if (this.x > other.x) return 1;
if (this.y < other.y) return -1;
if (this.y > other.y) return 1;
return 0;
}
If your array is sorted according to this comparison, you just need to keep the last entry with the same x in your array to get what you want. This means that you remove entries unless its successor has a different x.
This method is interesting of you don't want to keep your original data, but only keep the result: it will update the existing array in place, for a complexity of O(n) in time (not counting the sorting, which should happen anyway if I understand your question correctly).
Alternatively, the whole filtering can be achieved by applying a fold on your collection, where folding here is simply keeping the highest y for a same x (as you stated it precisely in your question). Because your collection is already sorted on x, it means that it is naturally partitioned on values of x, so you can build a new collection by accumulating the correct x for each partition in another array. For each element of the array, you compare it with the last inserted entry in your new array, if the x are the same, you replace it with the object with the highest y. If the x are different, you add it to the new array.
The advantage of this approach is that you don't need to change the sorting algorithm, but on the other hand you need a new array. This algorithm should therefore work in O(n) space and time.
Finally, this algorithm may be adapted to an in place update of the original array. It is slightly more complicated, but would let you avoid the extra allocation (crucial on embedded systems).
Pseudo code:
int idx = 0;
while (idx + 1 < Objarray.size()){
MyObj oi = Objarray.get(idx), on = Objarray.get(idx+1);
if (oi.x == on.x) {
if (on.y < oi.y)
Objarray.remove(idx++);
else
Objarray.remove(idx+1);
} else
idx++;
}
Note that while working in constant space, it might be slightly less efficient than the allocating algorithm, because of the way ArrayList works internally (though it should be better than using other usual container types).
/**
*
*/
package test1;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* #author raviteja
*
*/
public class UinquecutomObjects {
/**
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Employee e1=new Employee();
e1.setName("abc");
e1.setNo(1);
Employee e2=new Employee();
e2.setName("def");
e2.setNo(2);
Employee e3=new Employee();
e3.setName("abc");
e3.setNo(1);
List<Employee> empList=new ArrayList<Employee>();
empList.add(e1);
empList.add(e2);
empList.add(e3);
System.out.println("list size is "+empList.size());
Set<Employee> set=new HashSet<Employee>(empList);
System.out.println("set size is "+set.size());
System.out.println("set elements are "+set);
}
}
class Employee{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
private int no;
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + no;
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Employee other = (Employee) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (no != other.no)
return false;
return true;
}
}

How to fetch specific object from a set in Java

I would like quickly to fetch from Java Set the object equal to existing one. Is there any faster way than iterating for all elements of the set?
Here is my code:
class A {
int a,b,c,d;
public A(int a, int b, int c, int d) {
this.a = a;
this.b = b;
this.c = c;
this.d = d;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + getOuterType().hashCode();
result = prime * result + a;
result = prime * result + b;
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
A other = (A) obj;
if (!getOuterType().equals(other.getOuterType()))
return false;
if (a != other.a)
return false;
if (b != other.b)
return false;
return true;
}
private Main getOuterType() {
return Main.this;
}
}
and in the code:
void run() {
Set<A> a = new HashSet<>();
a.add(new A(1,2,3,4));
a.add(new A(2,3,4,5));
A b = new A(1,2,3,5);
//How to fetch from set a object equal to object b?
}
Is it possible to do it fast in Groovy?
There is not get method in java.util.Set interface. Hence, you can not fetch an entry :)
Maybe you are using the wrong data structure. May be what you need is a java.util.Map?
If you already have an object then there's no point in getting it from the Set. If you like to check if it exists in the set there is http://docs.oracle.com/javase/7/docs/api/java/util/Set.html#contains(java.lang.Object)

Java: Problems with TreeSet

I have a class Odp. I want to use TreeSet to keep a sorted collection of Odp objects. However, I've been having problems.
public class OdpStorage {
private TreeSet<Odp> collection = new TreeSet<Odp>();
public addOdp(Odp o) {
return collection.add(o);
}
public int size() {
return collection.size();
}
}
collection.add(Odp o) is supposed to do nothing if it's already in the tree, right? Somehow, this unit test fails:
OdpStorage ts = new OdpStorage();
Odp ftw = new Odp("LOL");
Odp ktr = new Odp("OMG");
ts.addOdp(ftw);
ts.addOdp(ftw); //should do nothing
ts.addOdp(ftw); //should do nothing
ts.addOdp(ftw); //should do nothing
ts.addOdp(ktr);
assertEquals(2, ts.size());
The assertion fails. It expects 2, but the return value is 5. Why? Could the odp.equals() function be messed up?
Similarly, calling collection.contains(o) fails, even when the there is an object in the set X for which o.equals(X) returns true.
The .equals() function of Odp: (generated by Eclipse)
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof Odp))
return false;
Gene other = (Odp) obj;
if (sequence == null) {
if (other.sequence != null)
return false;
} else if (!sequence.equals(other.sequence))
return false;
return true;
}
compareTo:
/**
* this = g0
* if they are equal, g1 is presumed to come first
*
* #return -1 if g0 comes before g1; 1 if g0 comes after g1
*/
#Override
public int compareTo(Odp g1) {
if (sequence.length() < g1.getSeq().length()) {
return -1;
}
else if (sequence.length() > g1.getSeq().length()) {
return 1;
}
if (sequence.compareTo(g1.getSeq()) < 0) {
return -1;
}
return 1;
}
hashCode() is not overridden. Problem?
UPDATE
hashCode() is as follows:
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((sequence == null) ? 0 : sequence.hashCode());
return result;
}
But that still doesn't solve the problem.
Your compareTo implementation never returns 0. It should return 0 when the object instances are equal.
It appears that your collection.add(o) is failing to find the object in the backing TreeMap. Does your Odp implement Comparable or are you setting a default Comparable on your TreeSet whose compare method you have implemented? If so, you will need to ensure that your compareTo (for the Comparable), or your Comparator compare method will return 0 if the objects passed in are equals.
EDIT (in response to your comment to the original post):
It is recommended that you override HashCode() whenever you override equals()
EDIT2 in response to your compareTo implementation:
If g0 and g1 are equal, you should return 0. This is the root of the problem.
Mate cleanup your equals, its got too many if/elses. replace it with a nice do/while with lots of condition tests. If all the tests pass then reutrn true...Yes its got "goto" statements but its very easy to read and even easier to insert new conditions as necessary without lots of nesting. Nesting if/elses is evil. Using "elses" is evil and almost always never needed.
#Override
public boolean equals(final Object object) {
boolean equals = false;
do {
if (this == object) {
equals = true;
break;
}
if (false == super.equals(object)) {
break;
}
final DocumentView view = Unsafe.cast(object);
if (false == this.document.equals(view.document)) {
break;
}
if (this.revision != view.revision) {
break;
}
if (false == this.user.equals(view.user)) {
break;
}
if (false == this.timestamp.equals(view.timestamp)) {
break;
}
equals = true;
} while (false);
return equals;
}

Categories

Resources