[英]Update a POJO property with 'position' in a SortedMap(TreeMap)
I am trying to simulate a game board where multiple players can submit their game scores. 我正在尝试模拟一个游戏板,多个玩家可以提交他们的游戏分数。
The POJO viz. POJO即 Entry.java represents an entry in the leaderboard.
Entry.java表示排行榜中的一个条目。 Note the overriden equals() method.
请注意 ,重写的equals()方法。
Position is the position in the leaderboard, 1 being the user with the highest score
位置是排行榜中的位置,其中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 + "]";
}}
I am using a TreeMap to store the entries, based on the score, they are sorted automatically 我正在使用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;
}
}
How do I set the 'position' field of an Entry ? 如何设置条目的“位置”字段? eg: Below are entries sorted as per the scores, how do I get the corresponding 'index/position' and set it in the entry ?
例如:以下是按分数排序的条目,如何获得相应的“索引/位置”并将其设置在条目中?
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]
Now, user1 entry should have position=1, user2 entry should have position=2 and so on. 现在,user1条目的位置应为= 1,user2条目的位置应为= 2,依此类推。
By my understanding, you should be able to convert your List to an Array using listArray = list.toArray()
, then use 据我了解,您应该能够使用
listArray = list.toArray()
将List转换为Array,然后使用
for (int i = 0; i < listArray.length; i++) { listArray[i].position = i + 1; }
To set their position to their index. 将其位置设置为其索引。 (Plus one, because the winner should be first, not zeroth)
(加一,因为获胜者应该是第一,而不是零)
You can't do that automatically in a TreeMap
, you will have to write that on your own, but this will not be cheap. 您不能在
TreeMap
中自动执行此操作,而必须自己编写,但这并不便宜。 On each update of a score
(I actually mean remove that entry and then place it back into the map) you will need to traverse the entire map
and update it accordingly. 每次更新
score
(实际上是指删除该条目,然后将其放回地图中),您将需要遍历整个map
并进行相应的更新。 You can do that with the entrySet()
iterator that says: The set's iterator returns the entries in ascending key order.
您可以使用显示以下内容的
entrySet()
迭代器来做到这一点: The set's iterator returns the entries in ascending key order.
Something like this: 像这样:
Iterator<...> it = map.entrySet().iterator();
int num = 1;
while(it.hasNext()) {
Entry en = it.next;
en.getKey().setPosition(num++);
}
Perhaps, you're better off with a sorted List
. 也许,最好使用Sorted
List
。
Consider 考虑
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();
}
}
By ensuring that the list is always sorted, you can exploit the sorted nature in lookup and insertion operations, so you'll have the same O(log n)
time complexity as TreeMap
, the required space might be even less for larger number of elements due to the flat array storage. 通过确保列表始终被排序,您可以在查找和插入操作中利用排序的性质,因此与
TreeMap
具有相同的O(log n)
时间复杂度,对于较大数量的元素,所需空间甚至更少由于平面阵列存储。 Wrapping the List
in another class like RankList
helps ensuring that the sorted property can't get invalidated by accident when modifying the List
. 将
List
包装在另一个类(如RankList
有助于确保在修改List
时不会使排序后的属性意外失效。
It's still required to remove and re-insert an element when changing its ordering property, but it's still straight-forward, eg 更改元素的排序属性时,仍然需要删除并重新插入元素,但是仍然很简单,例如
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);
When you're going to update all elements, however, it might be more efficient to perform the bulk update and instantiate a new RankList
afterwards… 但是,当您要更新所有元素时,执行批量更新并在之后实例化新的
RankList
可能会更有效……
Or add something like 或添加类似
public void updateAll(Consumer<? super T> updateElementAction) {
contents.forEach(updateElementAction);
contents.sort(order);
}
to RankList
if you can express the update action as Consumer
, letting the list temporarily go unsorted and validate with a single sort
operation afterwards. 如果可以将更新操作表示为
Consumer
,则将其RankList
给RankList
,让列表暂时不排序,然后再通过单个sort
操作进行验证。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.