一个游戏项目,服务器需要维护一个玩家的有序集合(排行榜),玩家的一些动作会改变自身的状态,比如等级改变。我希望在不使用 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 后反而元素变多了,即一开始就移除失败了。
所以我想问一下,有没有一种模式或者类能提供一种结构使得集合中元素值变化后,通过某种回调来使集合依旧有序?
요소를 먼저 제거한 다음 삽입할 수 있습니다.
클래스의 equals 및 hashCode 메소드를 다시 구현해야 할 수도 있습니다.
배열이나 연결리스트 사용법에 대해서는 걱정하지 마세요.
배열
int[] a=[10,6,2,0]
, 요소 a[3]의 값이 0에서 7로 변경될 때.은 다음과 같이 수행할 수 있습니다. 다른 요소의 상대 위치를 변경하지 않고 유지하고(즉,
Collections.sort()
을 사용하지 않음) a[3] 요소를 a[0] 뒤에 배치한 다음 전체 요소를 a 뒤에 배치합니다. [0] 한 위치 뒤로 이동합니다.안 좋아 보이지만 순위라고 생각하면 예를 들어 사용자가 1,000명이라면 위의 연산 횟수에 1,000을 곱해야 합니다.
이것은 동시성이므로 잠금이 반드시 포함되므로 성능이 낙관적이지 않을 수 있습니다.
저희 게임 플레이 경험을 생각해보면 순위가 실시간으로 갱신되지는 않습니다.
그러면 사용자 정보 상태가 바뀔 때마다 수정하는 것이 아니라 5분에 한 번씩
Collections.sort()
을 통해 구현해보겠습니다.