排序 - Java 中怎样实现一种即使元素改变依然有序的集合?
阿神
阿神 2017-04-17 17:40:57
0
2
683

一个游戏项目,服务器需要维护一个玩家的有序集合(排行榜),玩家的一些动作会改变自身的状态,比如等级改变。我希望在不使用 Collections.sort() 方法的情况下维持这个集合的有序状态。

我尝试了继承了 TreeSet 然后实现一个重新排序的回调 ReorderCallback,在任何玩家经验值改变的时候调用回调的方法 reorder() 来使集合(排行榜)保持有序,代码如下

interface ReorderCallback<T> {

    void reorder(T element);
}

class AlwaysOrderedSet<T> extends TreeSet<T> implements ReorderCallback<T> {

    // ...
    @Override
    public void reorder(T element) {
        remove(element);
        add(element);
    }
}

然后调用 AlwaysOrderedSet

AlwaysOrderedSet<Player> set = new AlwaysOrderedSet<>();

player.setExp(xxxxx);
set.reorder(player);

然而,每次玩家状态改变后调用 reorder() 并不能保持原集合的有序,反而会重复添加 player。因为 TreeSet 无法追踪元素的变化,就像以下的演示一样,

public class Sorter {

    public static void main(String[] args) {

        class Student implements Comparable<Student> {

            int id;
            String name;
            int age;

            Student(int id, String name, int age) {
                this.id = id;
                this.name = name;
                this.age = age;
            }

            @Override
            public String toString() {
                return String.format("id=%d, name=%s, age=%d", id, name, age);
            }

            @Override
            public int compareTo(Student o) {
                return o.age - this.age;
            }
        }

        Set<Student> alwaysOrdered = new TreeSet<>();

        Student a = new Student(1, "Amy", 50);
        Student b = new Student(2, "Bob", 30);
        Student c = new Student(3, "Chris", 40);

        alwaysOrdered.add(a);
        alwaysOrdered.add(b);
        alwaysOrdered.add(c);

        System.out.println("-- before --");
        alwaysOrdered.forEach(System.out::println);

        b.age = 100;

        System.out.println("-- after --");
        alwaysOrdered.forEach(System.out::println);

        alwaysOrdered.remove(b);
        alwaysOrdered.add(b);

        System.out.println("-- after remove and add --");
        alwaysOrdered.forEach(System.out::println);
    }
}

结果是

-- before --
id=1, name=Amy, age=50
id=3, name=Chris, age=40
id=2, name=Bob, age=30
-- after --
id=1, name=Amy, age=50
id=3, name=Chris, age=40
id=2, name=Bob, age=100
-- after remove and add --
id=2, name=Bob, age=100
id=1, name=Amy, age=50
id=3, name=Chris, age=40
id=2, name=Bob, age=100

对 b 的更改并没有改变其在集合中的位置。移除 b 再添加 b 后反而元素变多了,即一开始就移除失败了。

所以我想问一下,有没有一种模式或者类能提供一种结构使得集合中元素值变化后,通过某种回调来使集合依旧有序?

阿神
阿神

闭关修行中......

répondre à tous(2)
Ty80

Vous pouvez d'abord supprimer l'élément, puis l'insérer.
Les méthodes equals et hashCode de la classe devront peut-être être réimplémentées.

迷茫

Ne nous préoccupons pas des détails de l'utilisation des tableaux ou des listes chaînées.
Un tableauint[] a=[10,6,2,0], lorsque la valeur de l'élément a[3] passe de 0 à 7.
peut être fait comme suit : conserver la position relative des autres éléments inchangée (c'est-à-dire ne pas utiliser Collections.sort()), mettre l'élément a[3] après a[0], puis mettre l'élément entier après a [0] Reculer d'une position.

Ça n'a pas l'air bien, mais étant donné qu'il s'agit d'un classement, par exemple, s'il y a 1 000 utilisateurs, alors le nombre d'opérations ci-dessus sera multiplié par 1 000.
Il s'agit de la concurrence, qui implique définitivement le verrouillage, donc les performances peuvent ne pas être optimistes.

En pensant à notre propre expérience de jeu, les classements ne sont pas actualisés en temps réel.
Ensuite, nous le mettons toujours en œuvre via Collections.sort(), une fois toutes les 5 minutes, au lieu de le modifier à chaque fois que l'état des informations utilisateur change.

Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal