简体   繁体   中英

Java - store objects in a hierarchy that follows their class inheritance

I need to store lots of objects that belong to different classes:

ClassA {...}
ClassA1 extends ClassA {...}
ClassA2 extends ClassA {...}
ClassA2a extends ClassA2 {...}
ClassB {...}

Now I need to find a way to store all these objects in a way that allows me to efficiently get all objects that belong to a particular class and its inherited child classes. For example, this imaginary code

getObjects(ClassA2)

would return a list of all stored objects that belong to ClassA2 or ClassA2a.

I believe a tree collection of some sort would be suitable, but I can't think of any way to implement it. Any ideas?


(Background: I am creating a simple java game, in which there's number of sprites that I need to manage, while some of those sprites share similar properties. When I check for events like collisions, I need to get all objects that extend EnemySprite and compare their coordinates with the player's sprite.)

There are several ways how to approach this. One would be, eg, to generate strings like ParentClass1:ChildClass2:ChildClass1: for every object and use them as a key to a TreeMap or Trie which you would then traverse.

Here is a simpler solution, though. The following class contains a map from class to all objects implementing it. The only trick is adding an object to all buckets where it belongs:

public class HierarchyMap {
    private final Map<Class<?>, List<Object>> map = new HashMap<>();

    public void add(Object o) {
        Class<?> clazz = o.getClass();
        while (clazz != Object.class) {
            List<Object> list = map.computeIfAbsent(clazz, c -> new ArrayList<>());
            list.add(o);
            clazz = clazz.getSuperclass();
        }
    }

    public List<Object> getByClass(Class<?> clazz) {
        return map.get(clazz);
    }
}

Usage:

public class A { public String toString() { return "A"; } }
public class B extends  A{ public String toString() { return "B"; } }
public class C extends  B { public String toString() { return "C"; } }
// ...
HierarchyMap hierarchyMap = new HierarchyMap();
hierarchyMap.add(new A());
hierarchyMap.add(new B());
hierarchyMap.add(new C());
System.out.println(hierarchyMap.getByClass(B.class)); 
// prints [B, C]

Mifeet seems to have literally answered your question, but I suspect you shouldn't be trying to do what you're proposing to do. Why not just have a master list of all objects that might collide, then filter it as needed using instanceof ?

This is conceptually a lot easier than what you're proposing to do, and the efficiency impact probably isn't that big. (In general, you will probably hear or have heard the mantra: Don't try to optimize too early.)

To be honest, I'm not sure you realize that filtering for EnemySprite will get you all object instances of its subclasses as well.

public class CollisionChecker(){

   private List colliders;

   public CollisionChecker(){    
       colliders = new ArrayList<Object>();    
   }

   public void addCollider(Object o){
       colliders.add(o);
   }

   public List<EnemySprite> getEnemySprites(){
       List<EnemySprite> enemies = new ArrayList<EnemySprite>();
       for (Object o : colliders)
           if (o instanceof EnemySprite)
               enemies.add((EnemySprite)o);
       return enemies;        
   }     
}

If you're storing the objects in a List<Object> , call Class#isInstance() on each element, adding them to another List if isInstance() returns true .

List<Object> objects = new ArrayList<>();

public <T> List<T> getObjects(Class<T> desiredClass) {
    List<T> desiredObjects = new ArrayList<>();
    for (Object o : objects)
        if (desiredClass.isInstance(o))
            desiredObjects.add((T)o);
    return desiredObjects;
}

getObjects(EnemySprite.class); // call it like this

If you just want collision detection, then I would add them to a static collection in the ancestor class. This would be the most efficient solution.

If you want to all descendants for a class you should check out the reflection APIs. Yes, they're said to be slow, but I have doubts that it matters enough for things that aren't computed for every frame. And for things that you need in every frame tree traversal would inefficient anyway. (@Miffet's suggestion of string comparison would probably be even slower than regular reflection.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM