Использование моего собственного объекта в качестве ключа в TreeMap

Поскольку сортировка 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;
}

}

И ниже метод, который вызывает проблему:

Если идентификаторы пользователей совпадают, я хочу вернуть 0, чтобы избежать дублирования, поэтому, если я выполняю map.put (userHighscore), он должен автоматически заменяться, если на карте есть другой объект с тем же userId. Однако, если пользователи разные, я хочу, чтобы они были отсортированы по их значениям. Этот подход прекрасно работает для одного потока, однако мое приложение работает одновременно, и когда их больше, чем три, оно добавляет дубликаты на карту. Моя проблема с картой рекордов, которая является concurrentHasmap и внутри нее содержит древовидную карту.

Вы видите что-то не так с моим подходом?

2 ответа

Решение

Обновленный ответ

Глядя лучше на источник TreeMaphashCode это не настоящая проблема.

Проблема здесь

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

Этот код не является потокобезопасным также, если highScores это ConcurrentHashMap,

Здесь возможный сценарий

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(Object), то вызов метода hashCode для каждого из двух объектов должен привести к одному и тому же целочисленному результату.
  • Не требуется, чтобы, если два объекта были неравны в соответствии с методом equals(java.lang.Object), то вызов метода hashCode для каждого из двух объектов должен приводить к разным целочисленным результатам. Тем не менее, программист должен знать, что выдача различных целочисленных результатов для неравных объектов может улучшить производительность хеш-таблиц.

В вашем 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 

Проверка памяти против вызовов хеш-кода. Но должно быть хорошо для кэширования.

Другие вопросы по тегам