用于基于类型的查询的最佳数据结构是什么?

时间:2011-12-29 02:37:20

标签: java algorithm oop data-structures complexity-theory

我正在制作游戏,在这个过程中我遇到了一些问题。

我有许多不同类型的游戏元素。这些都是Entity类型。

有许多类型的实体,包括可见且需要在屏幕上绘制的实体。这些实体的类型为VisibleEntity,扩展了Entity

像这样,我在游戏中有许多不同的层次类型。

我的问题是:

在发出以下类型的查询时,哪种数据结构具有最佳的运行时复杂性?

1)获取Entity

类型的对象

2)获取VisibleEntity

类型的对象

3)获取SomeSpecificGameObject

类型的对象

简单的解决方案是将它们全部存储在一个列表中,并遍历它们全部,如果它们是指定的类型,则将它们添加到要返回的列表中。

显然,在屏幕中某些元素的每一帧都进行计算时,这是不可行的。

跟踪所有这些的最佳方法是什么?

我感谢任何回应!

3 个答案:

答案 0 :(得分:4)

另一个简单的解决方案是每种类型维护一个列表。将每个列表存储在散列映射中,类型为键。这是你可以获得的瞬间,每个项目只需要一个指针。它还可以将同一个对象添加到多个列表中。这将处理您的层次结构。

如果要减少上述方法所需的空间,请让每个实体列表对象也存储指向其子类列表的指针。这样,VisibleEntity将仅存在于VisibleEntity列表中,而不存在于其子类的列表中。要查找所有VisibleEntities,请对主列表执行哈希查找,然后通过子类对子类进行递归搜索。这是一个简单的树结构。

class MyEntityList {
    List<Entity> entityList = new ArrayList();
    List<MyEntityList> subclassLists = new ArrayList();
}

根据树的高度和对象的数量,第一种方法可能会更好。这是复杂性与时间对空间权衡的关系。

答案 1 :(得分:1)

我真的不认为使用类层次结构来指示对象是否具有某个属性是最好的主意。

您可能希望保留可以在octree中在游戏引擎中呈现的对象,当然他们需要有一些绘制方式;所以这可能是一个类型的好候选者(在某种意义上,接口是一种类型)。

对于其他任意属性,你可以一般性地表示它们,然后维护索引以便快速检索,或者就像你说的那样,将它们保存在列表中并有一个简单的过滤函数,它可以返回一个匹配特定对象的子集标准。如果没有太多的对象,那么第二种方法应该可以正常工作。 “太多”的阈值是您可能必须通过某些测试得出的值。

答案 2 :(得分:1)

  

显然,在屏幕中某些元素的每一帧都进行计算时,这是不可行的。

这实际上取决于对象的数量。现代cpu每秒循环几十亿次。假设检查并添加到列表需要10个周期并且每秒执行100帧,则对每个帧的1000个元素列表进行迭代将导致cpu利用率为0.1%。所以恰恰相反,天真的解决方案是非常可行的,除非你有数千个游戏对象,或者需要每帧多次查询类型。像往常一样,建议是实施可能有效的最简单的事情,衡量绩效影响,并且只有在影响很大时才进行优化。

那就是说,下一个最简单的事情就是:

abstract class Entity {
    private static Map<Class<?>, Set<? extends Entity>> byType = new HashMap<>();

    public static <E extends Entity> Set<E> allOfType(Class<E> clazz) {
        @SuppressWarnings("unchecked")
        Set<E> set = (Set<E>) byType.get(clazz);
        if (set == null) {
            set = new LinkedHashSet<E>();
            byType.put(clazz, set);
        }
        return set;
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    protected Entity() {
        for (Class c = getClass(); c != Object.class; c = c.getSuperclass()) {
            allOfType(c).add(this);
        }
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    protected void destroy() {
        for (Class c = getClass(); c != Object.class; c = c.getSuperclass()) {
            allOfType(c).remove(this);
        }
    }
}

你喜欢

for (VisibleEntity ve : Entity.allOfType(VisibleEntity.class)) {
    ve.render();
}

该方法可以推广到除了类之外的其他东西,只需对地图使用另一种密钥类型,并在适当的时间插入和删除类别集。