14

I know both the IdentityHashSet (via Collections#newSetFromMap(Map))and the LinkedHashSet. However, what I need is a combination of the two, a LinkedIdentityHashSet. I could not find any ready-made solution on the net. Anybody knows how to tweak this?

Thanks for advice!

Rafael Winterhalter
  • 42,759
  • 13
  • 108
  • 192

3 Answers3

8

The implementation techniques there don't marry up very well. LinkedHashMap adds linked listing to the entry objects of the map. IdentityHashMap uses a probing technique and so avoids having any entry objects.

There's a few ways to add "identity" characteristics to a collection/map.

  • Force the key type to behave correctly with final equals and hashCode methods. Really all reference-type types should have this, but never mind.
  • If you can't modify equals and hashCode but can modify the class, add a final field that is of a final class that holds a final references to the type you were going to use as the key. Use the new field as the key.
  • Store adaptor objects in the collection/map. You will need to create a new adaptor instance for each lookup. It just has its equals/hashCode methods to call ==/System.identityHashCode on the original object.
Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305
  • sometimes you just need IdentityHashMap, these are the cases where you have *both* a normal HashMap with some custom equality relation, but still need the identity equality relation in another context (i had this situation once) – kutschkem Jun 24 '13 at 13:40
4

One option is to use LinkedHashSet and a wrapper

class LinkedIdentityHashSet<E> extends AbstractSet<E> {
    Set set = new LinkedHashSet();

    static class IdentityWrapper {
        Object obj;

        IdentityWrapper(Object obj) {
            this.obj = obj;
        }

        public boolean equals(Object obj) {
            return this.obj == obj;
        }

        public int hashCode() {
            return System.identityHashCode(obj);
        }
    }

    public boolean add(E e) {
        return set.add(new IdentityWrapper(e));
    }
...
Evgeniy Dorofeev
  • 133,369
  • 30
  • 199
  • 275
  • 2
    The equals method should be comparing `this.obj` to `((IdentityWrapper) obj).obj`. – Mike Samuel Sep 26 '16 at 17:58
  • 1
    Since that line is simply checking reference equality, the cast is not necessary. The references will be the same (or different) at runtime, regardless of what type they are at compile time. – GreenGiant Sep 28 '16 at 16:43
  • 1
    I like the idea conceptually. Downvoted because of a bug mentioned by @MikeSamuel. – SlavaSt Feb 20 '18 at 05:16
0

You can implement a custom Map that uses Wrapper objects that use the identity semantics, and use a LinkedHashSet internally that uses these Wrapper Objects.

kutschkem
  • 7,826
  • 3
  • 21
  • 56