2016-12-07 5 views
1

Поскольку сортировка TreeMap основана только на ключах, я использую пользовательский объект в качестве ключа в treemap. Я уважал на мой взгляд, контракт между равными и CompareTo в этом случае, если два объекта равны, т.е CompareTo возвращает 0.Использование моего собственного объекта как ключа в TreeMap

Ниже код объекта:

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), он должен автоматически заменить, есть ли другой объект на карте с тем же Идентификатор пользователя. Однако, если пользователи отличаются друг от друга, я хочу, чтобы они отсортировались по их значениям. Этот подход работает отлично для одного потока, однако мое приложение является параллельным, и когда есть больше, чем триады, он добавляет дубликаты к карте. Моя проблема связана с картой рекордов, которая является одновременным Хасмапом и внутри нее содержит treemap.

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

+0

Если вы используете несколько потоков, какова ваша стратегия синхронизации? – vanje

+0

Возможно, вы захотите использовать карту из параллельного пакета https://docs.oracle.com/javase/tutorial/essential/concurrency/collections.html –

+0

Уже это делает. Я отредактировал вопрос, чтобы показать код, я использую concurrentHashmap – fgonzalez

ответ

1

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

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

Я думал, что переопределение хэш-кода было необходимо только для HashMaps и устанавливает – fgonzalez

+0

Но treeMap использует compareTo для равенства вместо hashCode, я должен все-таки переопределить hashcode? – fgonzalez

+0

@fgonzalez Я отредактировал свой ответ, как вы сказали, это не проблема, а проблема hashCode. –

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 

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

Смежные вопросы