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

时间:2017-09-14 12:20:55

标签: java collections java-8

我正在尝试模拟一个游戏板,其中多个玩家可以提交他们的游戏分数。

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,依此类推。

3 个答案:

答案 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操作进行验证。