按排序顺序存储条目并检索条目

时间:2017-09-13 13:29:53

标签: 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 + "]";
}}

GameBoard类有几种方法:

  • submitScore(String uid,int score)每个玩家都会调用此方法将其分数提交给游戏板。每个玩家/用户只有一个条目,因此,如果玩家多次调用此方法,则会存储他的最新分数
  • getLeaderBoard(String uid)
  

如果用户在排行榜中,则返回最多两个条目   比用户更高的分数(直接位于用户之上的用户)   用户在排行榜中),用户自己的条目和最多两个条目   紧接在排行榜中的用户之后

e.g:

The leader board is :
Entry [uid=user1, score=14, position=1]
Entry [uid=user2, score=8, position=2]
Entry [uid=user3, score=7, position=3]
Entry [uid=user4, score=7, position=3]
Entry [uid=user5, score=4, position=4]
Entry [uid=user6, score=3, position=5]
Entry [uid=user7, score=3, position=5]
Entry [uid=user8, score=1, position=6]

For user5, entries returned should be :
Entry [uid=user3, score=7, position=3]
Entry [uid=user4, score=7, position=3]
Entry [uid=user5, score=4, position=4]
Entry [uid=user6, score=3, position=5]
Entry [uid=user7, score=3, position=5]

For user4, entries returned should be :
Entry [uid=user1, score=14, position=1]
Entry [uid=user2, score=8, position=2]
Entry [uid=user4, score=7, position=3]
Entry [uid=user5, score=4, position=4]
Entry [uid=user6, score=3, position=5]

For user6, entries returned should be :
Entry [uid=user4, score=7, position=3]
Entry [uid=user5, score=4, position=4]
Entry [uid=user6, score=3, position=5]
Entry [uid=user8, score=1, position=6]

For user7, entries returned should be :

Entry [uid=user4, score=7, position=3]
Entry [uid=user5, score=4, position=4]
Entry [uid=user7, score=3, position=5]
Entry [uid=user8, score=1, position=6]

我最初的方法是使用TreeMap,另一种方法是here

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) {

        System.out.println("---------Current leader board---------");
        leaderBoardEntryUserMap.keySet().forEach(System.out::println);

        List<EntryMapOption> userEntryList = leaderBoardEntryUserMap.entrySet().stream()
                .filter(entry -> uid.equalsIgnoreCase(entry.getKey().getUid())).map(Map.Entry::getKey)
                .collect(Collectors.toList());

        if (userEntryList == null || userEntryList.isEmpty())
            return Collections.emptyList();

        // Incomplete and error prone
        EntryMapOption userEntry = userEntryList.get(0);

        List<EntryMapOption> entriesOptionTwo = new ArrayList<>();
        entriesOptionTwo.add(leaderBoardEntryUserMap.higherKey(userEntry));
        entriesOptionTwo.add(userEntry);
        entriesOptionTwo.add(leaderBoardEntryUserMap.lowerKey(userEntry));

        return entriesOptionTwo;

    }

}

以上代码的问题:

  • 何时(理想情况下,在submitScore()期间)以及如何计算“位置”。虽然它用于键,但我想知道Map.compute()是否可以以任何方式提供帮助!
  • 检查下面的代码'// Incomplete and error prone'评论 虽然'higherKey()'和'lowerKey()'很方便,但我不确定如何使用它们在特定条目下方和上方选择固定数量的条目

*****编辑-1 ****** @ Holger的解决方案解决了以下问题

  • 我无法弄清楚如何解决equals()和compare()之间的不一致问题。这导致缺少条目

1 个答案:

答案 0 :(得分:2)

地图的潜在问题

您的equals方法基于ID,但您的比较方法基于得分,这将导致JavaDoc出现问题。

  

请注意,如果此有序映射要正确实现Map接口,则树映射维护的顺序(如任何有序映射)以及是否提供显式比较器必须与equals一致。 (有关与equals一致的精确定义,请参阅Comparable或Comparator。)这是因为Map接口是根据equals操作定义的,但是有序映射使用compareTo(或compare)方法执行所有键比较,因此从排序映射的角度来看,通过此方法被视为相等的键是相等的。即使排序与equals不一致,也可以很好地定义有序映射的行为。它只是没有遵守Map接口的一般合同。

问题是比较方法会在错误的方向上进行搜索,因为它在不同的属性上进行排序。

在你的情况下,这可能不是一个问题,虽然你可能有2个相同的对象没有相同的分数,导致未来的问题。

使用TreeMap

的潜在问题

在项目位于树中时更改分数也可能会导致问题,因此您可能必须在每次分数更改时删除该项目,然后重新添加。

工作代码

这个过程的一个例子是

import java.util.Comparator;
import java.util.TreeMap;

public class Test {

  static class Example {
    final int id;
    final int score;

    Example(int id, int score) {
      this.id = id;
      this.score = score;
    }

    @Override
    public boolean equals(Object obj) {
      if (!(obj instanceof Example)) {
        return false;
      }
      final Example other = (Example) obj;
      return other.id == id;
    }

    @Override
    public int hashCode() {
      return id;
    }

    public int getId() {
      return id;
    }

    @Override
    public String toString() {
      return id + " scored " + score;
    }

    public int getScore() {
      return score;
    }
  }

  public static void main(final String... args) {
    Example a = new Example(1, 10);
    Example b = new Example(2, 30);
    Example c = new Example(3, 1);
    Example d = new Example(4, 10);

    TreeMap<Example, Integer> x = new TreeMap<Example, Integer>(Comparator.comparingInt(Example::getScore).thenComparing(Example::getId));

    x.put(a, a.getScore());
    x.put(b, b.getScore());
    x.put(c, c.getScore());
    x.put(d, d.getScore());

    final Example h2 = x.higherKey(a);
    final Example h1 = h2 == null ? null : x.higherKey(h2);

    final Example l1 = x.lowerKey(a);
    final Example l2 = l1 == null ? null : x.lowerKey(l1);

    System.out.println(h1);
    System.out.println(h2);
    System.out.println(a);
    System.out.println(l1);
    System.out.println(l2);
  }
}