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\$
  • 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. Oct 29 '15 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\$ – guitar_freak Oct 29 '15 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 Nov 8 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\$ – Mathieu Guindon Nov 8 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 Nov 8 at 15:51
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\$
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, privacy policy and cookie policy

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