0

我有一个每秒更新一次的 ArrayList 以进行一些基本检查并维护当前满足一组条件的玩家列表。

我想知道执行此操作的最高性能方法是我有 2 个建议的解决方案。

public void update() {
    for (Player player : Bukkit.getOnlinePlayers()) {
        if (!playersOnLadder.contains(player)) {
            if (checkPlayerOnLadder(player)) {
                playersOnLadder.add(player);
            }
        } else {
            if (checkPlayerOnLadder(player)) {
                playersOnLadder.remove(player);
            }
        }
    }
}

public void update() {
            playersOnLadder.clear();

    for (Player player : Bukkit.getOnlinePlayers()) {
        if (checkPlayerOnLadder(player)) {
            playersOnLadder.add(player);
        }
    }
}

在任何给定时间,这个数组列表中通常会有大约 75 名玩家。“在梯子上检查玩家的方法如下所示:

private boolean checkPlayerOnLadder(Player player) {
    int ladderAbsolute = this.getX()+this.getZ();
    int playerAbsolute = (int) player.getLocation().getX()+ (int) player.getLocation().getZ();

    //If the player is within 4 blocks (2 in each direction) of the ladder then return true.
    if (ladderAbsolute == playerAbsolute || (ladderAbsolute-2 > playerAbsolute && ladderAbsolute+2 < playerAbsolute)) {
        return true;
    } else {
        return false;
    }
}

EDITED 转换为 HashSet

4

4 回答 4

3

如果仅限于这两个选项,那么我会选择选项 2 作为性能更高的解决方案。

在选项 1 中,removean 中的操作ArrayList是 O(n),因为它必须找到玩家 O(n),如果找到则移除玩家,将每个人进一步向下移动到 O(n)。以这种方式循环遍历整个玩家列表是 O(n^2)。

选项 2 只是在 O(n) 处清除要开始的列表。该add操作是 O(1)(除非需要调整大小),因此将它们全部添加也是 O(n)。选项 2 总体上是 O(n)。

ArrayList用 a HashSet(并实施hashCodeand equalson )替换 yourPlayer可能是一种选择。散列操作是 O(1),所以遍历所有玩家并执行这些操作也是 O(n)。如果您以后需要Player快速找到一个特定的,然后使用 a HashSet,因为contains在 a 中是 O(1)HashSet而在 an 中是 O(n) ArrayList

于 2013-10-15T21:46:03.643 回答
1

请使用字典类型的数据结构进行这种簿记。HashSet(确保正确实现 hashCode!)很可能是您想要的。在语义和性能方面。

不过,我不明白你的梯子和伴随的算法。

于 2013-10-15T21:46:18.347 回答
1

最好的方法可能是使用 a HashSet,但不会覆盖,因此可以使用 Player 的entityIdPlayer hashCodeHashMap<int, Player>

像这样的东西:

HashMap<int, Player> playersOnLadder; /* make sure to initialize this */

public void update() {
    for (Player player : Bukkit.getOnlinePlayers()) {
        if (checkPlayerOnLadder(player)) {
            playersOnLadder.put(player.getEntityId(), player);
        } else {
            playersOnLadder.remove(player.getEntityId());
        }
    }
}

因为它是 a HashMap, puting 多次很好,并且HashMap.remove如果给定的对象不在地图中,则可以安全调用(它会返回null,但在这种情况下不需要检查它)。


比这更好的是更改您的代码以使用事件。看起来PlayerJoinEventPlayerQuitEvent可能对您有用,无论您的checkPlayerOnLadder函数做什么,都可以响应事件来完成。

于 2013-10-15T21:48:27.807 回答
0

“75 名玩家”+“每秒一次”=> 继续前进!

您可能每秒可以获得几纳秒,这可以使您的代码速度提高几 0.0000001%...

现在更快的选择可能是这样(分配通常比清算便宜):

public void update() {
    // pre sizing the list saves unnecessary resizing operations.
    playersOnLadder = new ArrayList<>(Bukkit.getOnlinePlayers());
    for (Player player : Bukkit.getOnlinePlayers()) {
        if (checkPlayerOnLadder(player)) {
            playersOnLadder.add(player);
        }
    } 
}
于 2013-10-15T22:04:05.683 回答