2017-09-13 70 views
0

我试图模拟多个玩家可以提交游戏分数的游戏板。按排序顺序存储条目并检索条目周围的条目

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

该游戏键盘类有两个方法:

  • submitScore(字符串uid,int score)每个玩家都会调用这个方法将他的分数提交给游戏棋盘。有每位玩家/用户只有一项,因此,如果如果用户是在排行榜玩家调用此方法多次,他的最新得分存储
  • getLeaderBoard(字符串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()是否能以任何方式提供帮助!
  • 检查下面的代码//不完整和容易出错的注释 虽然'higherKey()'和'lowerKey()'来得方便,但我不确定如何使用它们来选择固定数量的条目特别是进入

*****编辑-1 ****** @霍尔格的修复解决了以下问题

  • 我无法弄清楚如何解决平等之间的矛盾( )和compare()。这造成缺少项
+2

'Comparator.comparingInt(EntryMapOption :: getScore).reversed()。thenComparingInt(EntryMapOption ::的getuid)'... – Holger

+0

@Holger解决了第一个问题:) 更新原始比较代码。 –

+0

您不得修改影响其查找逻辑的关键字的属性,例如“HashMap”时的哈希码或“TreeMap”情况下的排序属性。如果它具有自然顺序,则在两种情况下都不能改变。您必须首先移除并在修改后重新添加,或者执行批量操作以创建新的“Map”。关于'higherKey'和'lowerKey',目前还不清楚你的问题在哪里。 – Holger

回答

2

潜在的问题与地图

你的equals方法是基于ID,但你的比较方法是基于分数,这会导致一些问题,根据JavaDoc。

请注意,如果此排序映射要正确实现Map接口,那么由树映射维护的排序(如任何已排序映射,以及是否提供显式比较器)必须与equals等效。 (请参阅Comparable或Comparator以获得与equals一致的精确定义)。这是因为Map接口是根据equals操作定义的,但有序映射使用其compareTo(或compare)方法执行所有关键比较,因此两个从排序映射的角度来看,这种方法认为相同的键是相等的。即使排序与等号不一致,排序映射的行为也是很好定义的;它只是不服从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); 
    } 
} 
+0

是的,我知道,我所缺少的是在TreeMap构造函数中使用比较器的正确方法,现在它已修复 –

+0

那么您是否建议TreeMap不是方式?哪些数据结构在正确性和性能方面会有所帮助? –

+0

TreeMap可以工作,但它需要以我的示例代码的形式出现,因为树会通过分数进行平衡,使查找速度更快,但更改了分数**需要**才能删除并重新读取,否则将会出现问题 – jrtapsell