Использование моего собственного объекта в качестве ключа в 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 ответа
Обновленный ответ
Глядя лучше на источник TreeMap
hashCode
это не настоящая проблема.
Проблема здесь
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
Проверка памяти против вызовов хеш-кода. Но должно быть хорошо для кэширования.