In the Java standard library, a LinkedHashSet
is the same as HashSet
but with predictable iteration order (insertion-order). I need an IdentityHashSet
(which uses object-identity or reference-equality, instead of object-equality) with predictable iteration order (insertion-order).
While there is no IdentityHashSet
in Java's standard library, you can easily create one using the available IdentityHashMap
with the following statement:
Set<T> identitySet = java.util.Collections.newSetFromMap(new IdentityHashMap<>());
This is almost the same implementation used in Guava's Sets.newIdentityHashSet()
; but this has the unspecified, generally chaotic ordering of HashMap
keys (and HashSet
elements).
My searches for an implementation of an IdentityLinkedHashSet
had no results, so I decided to implement it myself. One useful result which I found was this answer, which suggests to use an identity-wrapper class in composition with LinkedHashSet
.
I tried to implement this idea. Below is some key snippet of my implementation. Access the full Gist here.
public class IdentityLinkedHashSet<E> implements Set<E> {
private LinkedHashSet<IdentityWrapper> set;
/* ... constructors ... */
@Override
public boolean add(E e) {
return set.add(new IdentityWrapper(e));
}
@Override
public boolean contains(Object obj) {
return set.contains(new IdentityWrapper((E) obj));
}
/* ... rest of Set methods ... */
private class IdentityWrapper {
public final E ELEM;
IdentityWrapper(E elem) {
this.ELEM = elem;
}
@Override
public boolean equals(Object obj) {
return obj != null && ELEM == obj;
}
@Override
public int hashCode() {
return System.identityHashCode(ELEM);
}
}
}
Then I wrote a few unit-tests to verify my implementation. Unfortunately some of the assertions fail! Here are my tests:
String str1 = new String("test-1");
String str2 = new String("test-2");
String str3 = new String("test-2");
Set<String> identitySet = new IdentityLinkedHashSet<>();
assertTrue(idSet.add(str1));
assertFalse(idSet.add(str1)); // <-- fails!
assertTrue(idSet.contains(str1)); // <-- fails!
//
assertTrue(idSet.add(str2));
assertFalse(idSet.add(str2)); // <-- fails!
assertTrue(idSet.contains(str2)); // <-- fails!
assertFalse(idSet.contains(str3));
//
assertTrue(idSet.add(str3));
assertFalse(idSet.add(str3)); // <-- fails!
assertTrue(idSet.contains(str3)); // <-- fails!
assertEquals(3, idSet.size()); // <-- fails!
What have I done wrong in this implementation?