user697911
user697911

Reputation: 10551

How to implement a compareTo() method when consistent with Equal and hashcode

I have a class Product, which three variables:

class Product implements Comparable<Product>{
   private Type type;                 // Type is an enum
   Set<Attribute> attributes;        // Attribute is a regular class
   ProductName name;                 // ProductName is another enum
} 

I used Eclipse to automatically generate the equal() and hashcode() methods:

@Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((attributes == null) ? 0 : attributes.hashCode());
        result = prime * result + ((type == null) ? 0 : type.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;
        Product other = (Product) obj;
        if (attributes == null) {
            if (other.attributes != null)
                return false;
        } else if (!attributes.equals(other.attributes))
            return false;
        if (type != other.type)
            return false;
        return true;
    }

Now in my application I need to sort a Set of Product, so I need to implement the Comparable interface and compareTo method:

@Override
    public int compareTo(Product other){
        int diff = type.hashCode() - other.getType().hashCode();
    if (diff > 0) {
        return 1;
    } else if (diff < 0) {
        return -1;
    }

    diff = attributes.hashCode() - other.getAttributes().hashCode();

    if (diff > 0) {
        return 1;
    } else if (diff < 0) {
        return -1;
    }

    return 0;
    }

Does this implementation make sense? What about if I just want to sort the product based on the String values of "type" and "attributes" values. So how to implement this?

Edit: The reason I want to sort a Set of is because I have Junit test which asserts on the string values of a HashSet. My goal is to maintain the same order of output as I sort the set. otherwise, even if the Set's values are the same, the assertion will fail due to random output of a set.

Edit2: Through the discussion, it's clear that to assert the equality of String values of a HashSet isn't good in unit tests. For my situation I currently write a sort() function to sort the HashSet String values in natural ordering, so it can consistently output the same String value for my unit tests and that suffice for now. Thanks all.

Upvotes: 0

Views: 2563

Answers (2)

tsolakp
tsolakp

Reputation: 5948

Looks like from all the comments in here you dont need to use Comparator at all. Because:

1) You are using HashSet that does not work with Comparator. It is not ordered.

2) You just need to make sure that two HashSets containing Products are equal. It means they are same size and contain the same set of Products.

Since you already added hashCode and equals methods to Product all you need to do is call equals method on those HashSets.

HashSet<Product> set1 = ...
HashSet<Product> set2 = ...

assertTrue( set1.equals(set2) );

Upvotes: 1

Zabuzard
Zabuzard

Reputation: 25903

This implementation does not seem to be consistent. You have no control over how the hash codes look like. If you have obj1 < obj2 according to compareTo in the first try, the next time you start your JVM it could be the other way around obj1 > obj2.

The only thing that you really know is that if diff == 0 then the objects are considered to be equal. However you can also just use the equals method for that check.

It is now up to you how you define when obj1 < obj2 or obj1 > obj2. Just make sure that it is consistent.

By the way, you know that the current implementation does not include ProductName name in the equals check? Dont know if that is intended thus the remark.

The question is, what do you know about that attributes? Maybe they implement Comparable (for example if they are Numbers), then you can order according to their compareTo method. If you totally know nothing about the objects, it will be hard to build up a consistent ordering.

If you just want them to be ordered consistently but the ordering itself does not play any role, you could just give them ids at creation time and sort by them. At this point you could indeed use the hashcodes if it does not matter that it can change between JVM calls, but only then.

Upvotes: 0

Related Questions