使用我自己的对象作为TreeMap中的键

时间:2016-12-07 10:25:49

标签: java collections concurrency treemap sortedmap

由于TreeMap排序仅基于键,因此我使用自定义对象作为树形图中的键。在我看来,我尊重equals和compare之间的契约。在这种情况下,如果两个对象相等,则te,compareTo返回0。

在对象的代码下面:

public final class UserHighScore implements Comparable<UserHighScore>{

private final int userId;
private final int value;


public UserHighScore(int userId, int value) {
    this.userId = userId;
    this.value = value;
}

public int getUserId() {
    return userId;
}

public int getValue() {
    return value;
}


@Override
public boolean equals(Object obj) {
    if (obj == this) return true;
    if (!(obj instanceof UserHighScore)) {
        return false;
    }
    UserHighScore userHighScore = (UserHighScore) obj;
    return userHighScore.userId==userId;
}


@Override
public int compareTo(UserHighScore uh) {
    if(uh.getUserId()==this.getUserId()) return 0;
    if(uh.getValue()>this.getValue()) return 1;
    return -1;
}

}

在导致问题的方法之下:

如果用户ID是相同的我想要返回0以避免重复,那么如果我做map.put(userHighscore)它应该自动替换是否在地图中有另一个对象具有相同的userId 。 但是,如果用户不同,我希望它们根据其值进行排序。 这种方法对于一个线程非常合适,但是我的应用程序是并发的,当有多个线程时,它会向地图添加重复项。 我的问题是highscores map,它是一个concurrentHasmap,里面包含一个treemap。

你认为我的做法有什么问题吗?

2 个答案:

答案 0 :(得分:1)

更新回答

更好地了解TreeMap hashCode的来源不是一个真正的问题。

问题出在这里

if (highScores.get(levelId)==null) {
    highScores.put(levelId,Collections.synchronizedSortedMap(new TreeMap<UserHighScore,Integer>()));
}

如果highScoresConcurrentHashMap,则此代码也不是线程安全的。

这是一个可能的场景

Thread 1                                    Thread 2
----------------------------------------------------------------------
highScores.get(levelId) is null
                                            highScores.get(levelId) is null
highScores.put(levelId, ...);
                                            highScores.put(levelId, ...);

从这里开始,两个线程使用SynchronizedSortedMap的不同实例。

上一个回答

TreeMap不是Map的同步版本。

如果您在多线程环境中工作,则需要同步对TreeMap的访问权限。

TreeMap<UserHighScore> myTree = ...
...
UserHighScore userHighScore = ...
...
synchronized(myTree) {
    // Synchronize any access to myTree
    myTree.add(userHighScore);
}

但您还需要重新定义hashCode方法,因为您使用的是Map

  

返回对象的哈希码值。支持此方法是为了哈希表的优势,例如HashMap提供的哈希表。

请记住在合同之后重新定义hashCode

  
      
  • 每当在执行Java应用程序期间多次在同一对象上调用它时,hashCode方法必须始终返回相同的整数,前提是不修改对象的equals比较中使用的信息。从应用程序的一次执行到同一应用程序的另一次执行,此整数不需要保持一致。
  •   
  • 如果两个对象根据equals(Object)方法相等,则对两个对象中的每一个调用hashCode方法必须生成相同的整数结果。
  •   
  • 如果两个对象根据equals(java.lang.Object)方法不相等,则不需要在两个对象中的每一个上调用hashCode方法必须生成不同的整数结果。但是,程序员应该知道为不等对象生成不同的整数结果可能会提高哈希表的性能。
  •   

答案 1 :(得分:0)

在您的POJO对象中乘坐哈希码:

public int hashCode(){
    return  (userId + "").hashCode()

您也可以缓存哈希码。

private final int userId;
private final int userIdHash;
...


public UserHighScore(int userId, int value) {
    this.userId = userId;
    userIdHash = (userId + "").hashCode();
...

public int hashCode(){
    return userIdHash 

测试内存与哈希码调用。但是应该可以缓存。