4
\$\begingroup\$

I have many object of class Test. I want to be sure that among them, there are no two objects o1 and o2 where o1==o2. In order to achieve that, I want to add them all to a HashSet and check if hashSet.size() == numberOfInitialObjects .

In order to ensure that the potential future changes in equals method of the Test class will not affect my implementation, I wrote a simple wrapper so that instead of adding Test objects to the HashSet, the wrapper objects will be used. The code as such:

private class HashSetWrapper {

        private final Test component;

        public HashSetWrapper(Test component) {
            this.component = component;
        }

        @Override
        public int hashCode() {
            int hash = 7;
            hash = 89 * hash + Objects.hashCode(this.component);
            return hash;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
            final HashSetWrapper other = (HashSetWrapper) obj;
            return this.component == other.component;
        }
    }

Is my approach OK, or are there more suitable ways to achieve my goal?

\$\endgroup\$
6
  • 1
    \$\begingroup\$ I think you are worrying prematurely. Why will you think future changes in Test.equals() might break equivalence? If so, you have other things to worry about then. \$\endgroup\$
    – h.j.k.
    Commented Oct 29, 2015 at 10:08
  • 1
    \$\begingroup\$ @h.j.k. This part of app required to check if five Test values (test1, ..., test5) sent to a method (it means, potentially references to the same objects as well) "points" to different objects (objects can be equal in sense of equals method, but not ==). \$\endgroup\$
    – radekEm
    Commented Oct 29, 2015 at 13:22
  • 1
    \$\begingroup\$ All you have to do is override equals() method in your class. Every class extends Object class, and Object class has equals() method. When you override equals() method in your class, you can compare two objects of the same class. \$\endgroup\$
    – ASWIN S
    Commented Nov 8, 2019 at 11:32
  • 1
    \$\begingroup\$ @ASWINS I've converted your post into a comment, since it's not really reviewing the OP's code, which is what we expect of answers on this site. That said unless I'm mistaken, when overriding equals you should always also override hashCode, especially so when the intent is clearly to add a class to a HashSet, where each item's hashCode would be involved in the keying. At least, that's how it works in .net and I'd assume Java works similarly. \$\endgroup\$ Commented Nov 8, 2019 at 13:56
  • 1
    \$\begingroup\$ @MathieuGuindon - correct - overriding just the equals will cause issues in the HashSet where 2 values that are equal(), but have different hashCodes can exist. See the hashCode contract: docs.oracle.com/en/java/javase/13/docs/api/java.base/java/lang/… \$\endgroup\$
    – rolfl
    Commented Nov 8, 2019 at 15:51

2 Answers 2

10
\$\begingroup\$

Why build your own implementation when you can use the features available in the Java standard libraries?

IdentityHashMap gives you all the features you need, and a Java Stream/collector will allow you to extract the map easily from your collection....

Collection<Test> tests = .....
Map<Test,Test> uniques = tests.stream().collect(Collectors.toMap(
     t -> t,
     t -> t,
     (t,u) -> t,
     IdentityHashMap::new))

Now, if the size of the uniques is the same as the tests, you're good. The uniques Map is an IdentityHashMap where the key values are all identical instances (using ==). It supports a null key/value so that should be fine too.

\$\endgroup\$
0
2
\$\begingroup\$

A general improvement on "add everything then count" is to fail as soon as a duplicate is found.

  public static <T> boolean uniqueInstances(Collection<T> items) {
    Set<T> set = Collections.newSetFromMap(new IdentityHashMap<>()); // Stand-in for (non-existent) IdentitySet
    return items.stream().allMatch(set::add); // add() returns false if element already existed
  }
\$\endgroup\$

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.