views:

167

answers:

3

I need Set collection, where its items will be identified by items class. Something like ReferenceIdentityMap from Appache Collections, but on class scope i.e. two different instances of same class must be identified as same in this collection.

You know, it is a violation of equals()/hashCode() identity principle but in occasional use it makes sense.

I have done this in simple class backing with Map<Class<? extends E>, E>, but due to simplicity it doesn't implement Set<E>. There may be a more elegant solution, decorator of any Set<E> would be great.

Is there any implementation of such collection there (Apache/Google/something/... Collections)?

A: 

How about extending HashSet and overriding just the add(..) method, putting object.getClass() instead of the object itself in an inner Set<Class<? extends E>>, and if it succeeds, adding the item itself. Something like

public class ClassSet<E> extends HashSet<E> {
    private Set<Class<? extends E>> classSet = new HashSet<Class<? extends E>>();

    @Override
    public boolean add(E element) {
        if (classSet.add((Class<E>) element.getClass())) {
            return super.add(element); // this actually should always return true
        }
        return false;
    }
}
Bozho
`Set<Class<? extends E>>` is not `Set<E>`. I need collection of `E` items...
mschayna
@mschayna aha. see my update
Bozho
Yes, I have done something like this, actually with `Map<Class<? extends E>, E>` as I wrote in original question. I hope there is more elegant class out there :) in some public libraries, which I didn't found yet. BTW, Sean Owen's solution looks promising...
mschayna
@mschayna you said the problem of your solution is that it doesn't implement `Set`. This one does.
Bozho
You are right, sorry. Your solution is also acceptable.
mschayna
addition: Your solution is based on specific ancestor `HashSet`, it would be nice to have something like decorator pattern there. Suggested `Set<Wrapper<E>>` is a bit more flexible.
mschayna
+1  A: 

You wish to override the meaning of equals() / hashCode() for your set members. The cleanest way to do this, I imagine, is to use a wrapper class:

class Wrapper<E> {

  private final E item;

  Wrapper(E item) {
    this.item = item;
  }

  E getItem() {
    return item;
  }

  public boolean equals(Object o) {
    if (!(o instanceof Wrapper)) {
      return false;
    }
    return getClass().equals(o.getClass());
  }

  public int hashCode() {
    return getClass().hashCode();
  }

}

You would create a Set<Wrapper<E>> then.

Sean Owen
I accept this because it is flexible and usable with more collections (Set, Map...) and different implementations (Hash, Linked...). Thank you.
mschayna
A: 

You can create a Comparator class and construct your set with it in mind. The only condition you must not violate is that for every two elements you try to add, compare(e1, e2) should not throw a ClassCastException - which means that every two members you would try to insert should be comparable.

The comparator class itself should look only at the objects' classes, so it will be safe.

Check out the constructor here.

pnt
Don't think this will work as a `Comparator` is not used to check `Set` membership.
Sean Owen