简体   繁体   English

在SortedMap(TreeMap)中使用“ position”更新POJO属性

[英]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 ,则将其RankListRankList ,让列表暂时不排序,然后再通过单个sort操作进行验证。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM