Java - 高效的集合管理

时间:2015-02-12 04:15:35

标签: java collections

假设我有理由要求通过多种值类型快速查找类实例,为了解释这一点,我将以游戏服务器为例。

假设服务器使用静态标识号处理用户。此号码用于与特定玩家进行交流和互动(即:私人聊天,交易请求,战斗,公会邀请等)。

根据我目前的经验,这需要经常使用他们的识别号码查看玩家,最好的方法就是这样:(如果我错了,请纠正我。)

HashMap<Integer, Player>

然而,当处理网络时,很多时候我还需要让玩家与网络会话相关联,或者“插座”,因为有些人可能更熟悉。看起来像这样:

HashMap<Connection, Player>

所以我想弄清楚的是,我应该走这条路吗?

HashMap<Integer, Player> playersById;
HashMap<Connection, Player> playersByConnection;

或者我应该多做一些“捣乱”,如下:

HashMap<Object[], Player> playersOnline;

并将Object[0]作为整数,Object[1]作为连接,然后使用查找期间所需的那个。

或者这两种方式都不合适且不正确,是否有更好/更快的方式通过Integer或Connection查找它们而不重复收集?

任何见解都会非常感激。

编辑另外,是否有任何反对让HashSet<>HashMap<>包含相同的类引用?我注意到HashSet<>在迭代方面比HashMap<>更有效率,并且一直保持一个Map for lookup和一个Set for iterating,这是不好的做法?

3 个答案:

答案 0 :(得分:1)

我肯定会建议你为两个不同的搜索分别制作地图。他们真的是完全不同的独立需求。您也可能稍后需要添加新方法来查找玩家(按名称,按位置或游戏实例)。您不希望必须返回并继续更改现有的工作数据结构。

我的建议是将两个搜索地图封装在包含玩家或连接列表的类中。这样,它们只是这些类中的内部实现细节,而不是Player类(例如)需要担心的内容。

所以,例如:

class PlayerPopulation {
    private final List<Player> playerList = new ArrayList<>();
    private final Map<Player.ID, Player> playerByID = new HashMap<>();

    public void addPlayer(Player player) {
        playerList.add(player);
        playerByID.put(player.getID(), player);
    }

    public Player getPlayerByID(Player.ID id) {
        return playerByID.get(id);
    }
}

相同的模式将用于ConnectionPool(或者调用任何连接容器)。这样,您可以轻松添加搜索玩家的新方法,而无需担心您正在使用的地图结构。您还可以轻松地转换为HashSet或其他任何内容,而不会在一个类之外受到任何影响。如果您尝试使地图支持多个搜索路径,则无法执行此操作。

我还将ID更改为内部类而不是假设Integer。我意识到你只是举了一个例子,但认为这是良好封装的另一个例子:你可以改为Long而不改变PlayerPopulation类。

所以是的,我绝对建议不要将搜索键混合在一起。

答案 1 :(得分:-1)

首先,我必须提到宝贵的What Collection should I use流程图。


绝对将两个HashMap /非“smashed”版本用于两个独立的地图,然后在更高级别的类中将其抽象出来,可以随意打碎 - 例如使用静态实用函数或更有用“用法“对象。

你的第二个(“粉碎”)版本试图抽象出这个双重查找。 但是你需要两个快速且独立的查找。不要强迫你在应用程序级别需要/想要的抽象到数据级别。

  

或者这两种方式都不合适且不正确,是否有更好/更快的方式通过Integer或Connection查找它们而不复制集合?

复制集合可能是好事还是坏事。如果它使你的代码更优雅和可理解,那就很好。

Premature optimization is the root of all evil

  

此外,还有什么反对拥有HashSet&lt;&gt;和HashMap&lt;&gt;包含相同的类引用?

     

我注意到HashSet&lt;&gt;迭代的效率远远高于HashMap&lt;&gt;并且一直保持一个Map for lookup和一个Set for iterating,这是不好的做法吗?

只要它使你的代码更优雅和易懂,做任何这些事情都没有错。

答案 2 :(得分:-1)

创建一个包装器对象并在其中包含这两个对象。不要忘记在创建对象时覆盖hashMap()和equals()方法,因为您计划将其用作Map中的键。根据两个对象hashCode计算hasCode,并根据包含的实例计算equals。 请参阅此答案:equals and hashcode