我正在尝试模拟一个游戏板,其中多个玩家可以提交他们的游戏分数。
POJO即。 Entry.java表示排行榜中的条目。 注意覆盖equals()方法。
排名是排行榜中的位置,1是用户的排名 得分最高
public class EntryTreeMapOption {
private String uid;
private int score;
private int position;
public EntryTreeMapOption(String uid, int score) {
this.uid = uid;
this.score = score;
}
public EntryTreeMapOption() {
}
public String getUid() {
return uid;
}
public void setUid(String uid) {
this.uid = uid;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
public int getPosition() {
return position;
}
public void setPosition(int position) {
this.position = position;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((uid == null) ? 0 : uid.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
EntryTreeMapOption other = (EntryTreeMapOption) obj;
if (uid == null) {
if (other.uid != null)
return false;
} else if (!uid.equals(other.uid))
return false;
return true;
}
@Override
public String toString() {
return "Entry [uid=" + uid + ", score=" + score + ", position=" + position + "]";
}}
我使用TreeMap来存储条目,根据分数,它们会自动排序
public class GameDefault2 {
private TreeMap<EntryMapOption, String> leaderBoardEntryUserMap;
{
leaderBoardEntryUserMap = new TreeMap<>(Comparator.comparingInt(EntryTreeMapOption::getScore).reversed()
.thenComparing(EntryTreeMapOption::getUid));
}
@Override
public void submitScore(String uid, int score) {
EntryMapOption newEntry = new EntryMapOption(uid, score);
leaderBoardEntryUserMap.put(newEntry, uid);
}
@Override
public List<EntryMapOption> getLeaderBoard(String uid) {
List<EntryMapOption> userEntryList = .....
.....
.....
return entriesOptionTwo;
}
}
如何设置&#39;位置&#39;一个条目的领域?例如:下面是根据分数排序的条目,我如何得到相应的索引/位置&#39;并在条目中设置它?
Entry [uid=user1, score=14, position=0]
Entry [uid=user2, score=8, position=0]
Entry [uid=user3, score=7, position=0]
Entry [uid=user4, score=7, position=0]
Entry [uid=user5, score=4, position=0]
Entry [uid=user6, score=3, position=0]
Entry [uid=user7, score=3, position=0]
Entry [uid=user8, score=1, position=0]
现在,user1条目应该有position = 1,user2条目应该有position = 2,依此类推。
答案 0 :(得分:0)
根据我的理解,您应该能够使用listArray = list.toArray()
将列表转换为数组,然后使用
for (int i = 0; i < listArray.length; i++) {
listArray[i].position = i + 1;
}
将其位置设置为其索引。 (另外一个,因为获胜者应该是第一名,而不是第0名)
答案 1 :(得分:0)
你不能在TreeMap
中自动执行此操作,您必须自己编写,但这并不便宜。在score
的每次更新(我实际上是指删除该条目然后将其放回地图)时,您需要遍历整个map
并相应地更新它。您可以使用entrySet()
迭代器执行此操作:The set's iterator returns the entries in ascending key order.
类似这样的内容:
Iterator<...> it = map.entrySet().iterator();
int num = 1;
while(it.hasNext()) {
Entry en = it.next;
en.getKey().setPosition(num++);
}
答案 2 :(得分:0)
也许,你最好使用排序List
。
考虑
public class RankList<T> extends AbstractCollection<T> {
private final Comparator<T> order;
private final List<T> contents;
public RankList(Comparator<T> order) {
this.order = Objects.requireNonNull(order);
contents = new ArrayList<>();
}
public RankList(Comparator<T> order, List<? extends T> initialContents) {
this.order = Objects.requireNonNull(order);
contents = new ArrayList<>(initialContents);
contents.sort(order);
}
@Override
public boolean add(T e) {
int index = Collections.binarySearch(contents, e, order);
if(index>=0) return false;
contents.add(~index, e);
return true;
}
public int addAndGetIndex(T e) {
int index = Collections.binarySearch(contents, e, order);
if(index>=0) throw new IllegalStateException("duplicate element");
index = ~index;
contents.add(index, e);
return index;
}
@Override
public boolean remove(Object o) {
T t = (T)o;
int index = Collections.binarySearch(contents, t, order);
if(index<0) return false;
contents.remove(index);
return true;
}
@Override
public boolean contains(Object o) {
T t = (T)o;
return Collections.binarySearch(contents, t, order)>=0;
}
public int indexOf(T element) {
int ix = Collections.binarySearch(contents, element, order);
return ix<0? -1: ix;
}
public List<T> asList() {
return Collections.unmodifiableList(contents);
}
@Override
public Iterator<T> iterator() {
return contents.iterator();
}
@Override
public int size() {
return contents.size();
}
}
通过确保列表始终排序,您可以在查找和插入操作中利用排序的特性,因此您将具有与O(log n)
相同的TreeMap
时间复杂度,所需的空间可能是由于扁平阵列存储,因此对于更大数量的元件更少。将List
包装在RankList
这样的另一个类中有助于确保在修改List
时,排序的属性不会被意外失效。
在更改其排序属性时仍然需要删除并重新插入元素,但它仍然是直截了当的,例如。
RankList<EntryOption> rankList=new RankList<>(
Comparator.comparingInt(EntryOption::getScore).reversed()
.thenComparing(EntryOption::getUid));
ThreadLocalRandom r = ThreadLocalRandom.current();
for(int id=1; id<100; id++)
rankList.add(new EntryOption(String.valueOf(id), r.nextInt(100)));
EntryOption justAnother = new EntryOption("101", r.nextInt(100));
int pos = rankList.addAndGetIndex(justAnother);
int rangeStart = Math.max(0, pos-2), rangeEnd=Math.min(rankList.size(), rangeStart+5);
System.out.println("entries around "+justAnother);
rankList.asList().subList(rangeStart, rangeEnd)
.forEach(System.out::println);
System.out.println("update score of "+justAnother);
rankList.remove(justAnother);
justAnother.score+=20;
rankList.add(justAnother);
System.out.println("entries around "+justAnother);
pos = rankList.indexOf(justAnother);
rangeStart = Math.max(0, pos-2); rangeEnd=Math.min(rankList.size(), rangeStart+5);
rankList.asList().subList(rangeStart, rangeEnd)
.forEach(System.out::println);
但是,当您要更新所有元素时,执行批量更新并在之后实例化新的RankList
可能更有效...
或添加类似
的内容public void updateAll(Consumer<? super T> updateElementAction) {
contents.forEach(updateElementAction);
contents.sort(order);
}
到RankList
如果您可以将更新操作表达为Consumer
,则暂时不对该列表进行排序,然后使用单个sort
操作进行验证。