0

i have a class heirarchy,

Class b extends class a 
Class c extends class a

All the three classes can be instantiated. Now objects of these three types are added to a Set.

I am required to traverse this set and find out the instances of type c and then do some processing on that.

Currently I am traversing the set and checking each entry if its a instanceof c .

Is there a better way of doing this? Something in the Collections library itself?

Matt Wilko
  • 25,893
  • 10
  • 85
  • 132
Som Bhattacharyya
  • 3,504
  • 28
  • 47

5 Answers5

1

You didn't specify your desired behavior exactly. It sounds like you only want the exact instances of c, not instances of any class derived from c.

If that is the case, use Object.getClass:

x.getClass().equals(c.class);

This will be true if x is an instance of c, and not true if x is an instance of a class derived from c.

If that is not the case, use instanceof:

x instanceof c

This will be true if x is an instance of c, and also true if x is an instance of a class derived from c.

Whichever one you pick, from there you want to use an iterator:

Iterator<a> iter = set.iterator();
while(iter.hasNext()) {
    a item = iter.next()
    if(item[.getClass().equals(c.class)| instanceof c]) {
        // do something with item
    }
}

Of course, this is a common pattern, and there are libraries that encapsulate this logic.

Community
  • 1
  • 1
jason
  • 220,745
  • 31
  • 400
  • 507
  • yeah well i want want exact instances of c . – Som Bhattacharyya Jun 20 '13 at 13:22
  • you need isassignablefrom http://stackoverflow.com/questions/496928/what-is-the-difference-between-instanceof-and-class-isassignablefrom - also while == is ok in this case, it is really bad practice to not use .equals to compare anything that is non-primitive in Java – Tom Carchrae Jun 20 '13 at 13:26
  • @Tom Carchrae: No, `Class.isAssignableFrom` isn't needed here; he knows the type at compile time, so comparing the result of `Object.getClass` to `c.class` is okay. `Class.isAssignableFrom` is useful when you don't know the type at compile time. – jason Jun 20 '13 at 13:33
  • uh - rather, you don't need isAssignableFrom - somehow i read that wrong. in practice, i would avoid using instanceof because someone who extends your class will get a surprise. – Tom Carchrae Jun 20 '13 at 13:36
1

You might want to look into lambdaj. It lets you manipulate collections in a functional programming-esque way. Make a predicate like so:

Matcher<A> f = new Predicate<A>() {
    public boolean apply(A item) {
        return item instanceof C;
    }
};

And then just filter using that predicate. Simple!

filter(f, collection)

For more, here's a blog post outlining various methods: http://guidogarcia.net/blog/2011/10/29/java-different-ways-filter-collection/

Imre Kerr
  • 2,084
  • 10
  • 30
1

Not sure if this is a possibility for you but you could use Google Guava and use a filter on that set. Have your Predicate do the instanceof logic and then if true it will filter the set based on that.

http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/Sets.html

bhcmoney
  • 590
  • 3
  • 8
  • 19
1

You can optionnally index classes of each inserted objects but it mainly depend on how many items are in the Set and the rate of C instances.

Here is a sample class :

public class IndexedSet<T> {
  private Map<T,T> container = new HashMap<T,T>();
  private Map<Class<?>, Map<T,T>> indexByClass = new IdentityHashMap<Class<?>, Map<T,T>>();

  public boolean add(T e) {
    if (e == null) {
      throw new IllegalArgumentException("Can't add null");
    }
    if (container.containsKey(e)) return false;

    container.put(e, e);
    Map<T,T> indexEntry = indexByClass.get(e.getClass());
    if (indexEntry == null) {
      indexEntry = new IdentifyHashMap<T,T>();
      indexByClass.put(e.getClass(), indexEntry);
    }
    indexEntry.put(e,e);
    return true;
  }

  public boolean remove(T e) {
    e = container.remove(e);
    if (removed == null) return false;

    Map<T,T> indexEntry = indexByClass.get(e.getClass());
    indexEntry.remove(e);

    return true;
  }

  public Set<T> getAll() {
    return Collections.unmodifiableSet(container.keySet());
  }

  public Set<T> getByClass(Class<?> clazz) {
    Map<T,T> indexEntry = indexByClass.get(clazz);
    return indexEntry != null ? Collections.unmodifiableSet(indexEntry.keySet()) : null;
  }
}
LoganMzz
  • 1,523
  • 2
  • 17
  • 31
1

You can use a SetMultimap<Class<?>, A> to map from the concrete class to a set of instances of that class, and still have the containsValue methods which would correspond to the contains method in your original set.

Mike Samuel
  • 109,453
  • 27
  • 204
  • 234