【问题标题】:Using my own object as key in a TreeMap使用我自己的对象作为 TreeMap 中的键
【发布时间】:2017-04-22 05:57:07
【问题描述】:

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

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

【问题讨论】:

标签: java collections concurrency treemap sortedmap


【解决方案1】:

更新答案

更好地查看TreeMaphashCode的来源并不是真正的问题。

问题来了

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 方法必须产生不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同的整数结果可能会提高哈希表的性能。

【讨论】:

  • 我认为只有 HashMap 和集合需要 hashCode 重新定义
  • 但是treeMap使用compareTo而不是hashCode来表示相等,那我还是应该重新定义hashcode?​​span>
  • @fgonzalez 我编辑了我的答案,正如你所说,这里的 hashCode 函数不是真正的问题。
【解决方案2】:

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

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

【讨论】:

  • tgkprog 如果我们正在处理树图,我们真的需要覆盖哈希码吗?
猜你喜欢
  • 2015-07-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多