0

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.

12
  • 1
    Why would you ever use a hashCode within compareTo? Makes no sense. What would be the need to sort by hashCode? How is that a "natural" ordering of the class? Commented Jun 28, 2017 at 21:35
  • Ok. doesn't make sense. How to implement the natural ordering of the class? Commented Jun 28, 2017 at 21:36
  • So to answer your question, no your implementation makes absolutely no sense. Commented Jun 28, 2017 at 21:36
  • How do you want it ordered? By what criteria? That's what matters most, and that's what you should use within the compareTo. Commented Jun 28, 2017 at 21:36
  • @user697911: Does your class logically have a natural ordering? What do you want to sort by? Commented Jun 28, 2017 at 21:37

2 Answers 2

1

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) );
Sign up to request clarification or add additional context in comments.

5 Comments

that's true, but in JUnit test, my method returns a set of Product, and it's not convenient for me to create another Set<Product> to compare to, because it not straighforwd to create the Product object in Junit test, since Product contains other objects. The easiest way to assert the equality of the returned Hashset is to use the String value, since I can directly see the String value in Eclipse Junit. And that's why I want to assert on String values of HashSet.
Then just call equals on the returned Sets. You should never rely on toString except for human visualization of that object. The toString on Set can change in between JUnit runs on different machines and platforms.
To equals what? I have the returned Set, which is fine, but then compare it to what? I don't have another Set to be compared with Equal, and that's why I use the String value of the Set to compare, because the String value is known and is easy to get, but to create another Set in my Junit test code is hard.
Again, the right way is to compare 2 sets. One that is returned from your method and another one that you need to create either manually by adding Products to it or you can parse whatever string representation you have into a set of Products.
@tsllakp, that makes sense. What I did for now is to write a sort() function to sort the hashset string values in natural ordering, so that every time it can consistently generate the same string representation.
0

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.

6 Comments

Yes, the actual ordering doesn't matter much. I just want to achieve consistent HashSet output based on any ordering.
LinkedHashSet always outputs in the order you have added the elements, maybe that is enough. HashSet itself does not maintain any order, TreeSet holds its content sorted by using compareTo. Your hashcode implementation defines an ordering that is consistent as long as you do not restart the JVM. At the next start the order will probably be different, only equal elements will stay equal but the rest will change.
LinkedHashSet will have a performance impact, and that's not what I want. What's the right way to ensure correct and same order of a Product set?
The only difference between HashSet and LinkedHashSet is that it will hold the insertion order (by a LinkedList). It is not possible to get the advantages of a HashSet (constant get access) by also holding an order. Are you sure you need a HashSet? Maybe the implementations of SortedSet are more what you need, for example a TreeSet which holds its elements sorted by their compareTo or a Comparator. In any case you as the programmer need to specify the ordering. What are your exact needs? Is the question still unanswered?
I only need the ordering in TestCase for JUnit to assert on HashSet String values.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.