2017-09-13 71 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