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

Using my own object as key in a TreeMap

由于 TreeMap 排序仅基于键,因此我使用自定义对象作为树图中的键。在我看来,在这种情况下,我尊重 equals 和 compareTo 之间的契约,如果两个对象相等,te compareTo returns 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 相同,我想 return 0 以避免重复,所以如果我这样做 map.put(userHighscore) 它应该自动替换地图中是否有另一个对象具有相同的用户 ID。 但是,如果用户不同,我希望根据他们的价值观对他们进行排序。 这种方法对于一个线程来说工作得很好,但是我的应用程序是并发的,当有多个威胁时,它会向地图添加重复项。 我的问题是高分图,它是一个 concurrentHasmap,里面包含一个树图。

你觉得我的方法有什么问题吗?

更新答案

更好地查看 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:

Returns a hash code value for the object. This method is supported for the benefit of hash tables such as those provided by HashMap.

记得重新定义合约后面的hashCode

  • Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.
  • If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
  • It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables.

在您的 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 

测试内存与哈希码调用。不过缓存应该没问题。