Модераторы: LSD, AntonSaburov
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Проверить объекты на equals 
:(
    Опции темы
garbuz
Дата 26.9.2009, 04:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 677
Регистрация: 22.1.2008

Репутация: 8
Всего: 11



Есть код, который достает из базы через хибернейт два оъекта - юзера и проджект. У проджекта есть коллекция юзеров. Перед добавление пользователя в проект, проверяю его наличие в этом проекте, хотя даже если такой пользователь имеется, то все равно происходит добавление.
Код

            Long userId = Long.parseLong(request.getParameter("userId"));
            User user = userService.getById(userId);
            Long projectId = Long.parseLong(request.getParameter("projectId"));
            Project project = projectService.getById(projectId, true);
           
            if (!project.getUsers().contains(user)) {
                project.addUser(user);
                projectService.update(project);
            } else {
                error = "User already exists in this project";
            }


Судя по дебаггеру, не срабатывает
Код

 if (!project.getUsers().contains(user)) {

У проджекта коллекция юзеров проинициализирована. Пробовал проверять объекты юзеров из проджекта на equals с текущим пользователем, тоже нифига не equals. В чем дело? Объекты разные получаются? У объектов поля одинаковые, хотя как у одного, так и у другого есть не проинициализированные поля. У энтити юзер нет ни метода hashCode ни equals. Может в них дело?
Спасибо.
PM MAIL   Вверх
Galaran
Дата 26.9.2009, 08:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 91
Регистрация: 25.5.2009

Репутация: нет
Всего: 2



contains не срабатывает, так как надо метод equals() переопределить у класса юзер, иначе оно использует equals() класса Object, который просто по ссылке сравнивает.

Когда будешь писать сравнение для юзеров, не наткнись на то, что я недавно напоролся. А именно, если id у тебя там Long (не long), и ты сравниваешь по нему, надо сначала взять значение у этих лонгов, иначе по ссылке будет.
Примерно так:

Код

public boolean equals(Object obj) {
            if (obj instanceof Subject) {
                  Subject subj = (Subject)obj;
                  // Value is Long, not long
                  if (subjectId.longValue() == subj.getSubjectId().longValue())
                        return true;
            }
            return false;
}

PM MAIL   Вверх
garbuz
Дата 26.9.2009, 17:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 677
Регистрация: 22.1.2008

Репутация: 8
Всего: 11



Парсер лохе! Первый раз не отправил мое сообщение smile

Galaran, спасибо за совет, так и думал сделать, однако вчера на это не хватило сил (5-ый час). Сделал сегодня - не работает. Дело в том, что при получении дочерней коллекции из объекта, вытащенного с пом хибернейт, мы получаем объект PersistentSet, где метод contains почему-то не работает. На это есть даже баг в джире http://opensource.atlassian.com/projects/h...browse/HHH-2634 и пара тем на форумах https://forum.hibernate.org/viewtopic.php?t=965543, https://forum.hibernate.org/viewtopic.php?t=928172,  которые пока не прочитал smile Выход есть. Надо коллекцию перебирать итератором, и потом каждый объект сравнивать. Вот собственно так и сделал.
Код

 Long userId = Long.parseLong(request.getParameter("userId"));
            User user = userService.getById(userId);
            Long projectId = Long.parseLong(request.getParameter("projectId"));
            Project project = projectService.getById(projectId, true);
            Set<User> users = project.getUsers();
            boolean found = false;
            Iterator iterator = users.iterator();
            while (iterator.hasNext()){
                User u = (User) iterator.next();
                if (u.equals(user)){
                    found = true;
                    break;
                }
            }
            if (!found) {
                project.addUser(user);
                projectService.update(project);
            } else {
                error = "User already exists in this project";
            }


Думаю есть более красивое решение smile
PM MAIL   Вверх
MisterCleric
Дата 28.9.2009, 15:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1043
Регистрация: 16.2.2006
Где: Харьков, Украина

Репутация: 33
Всего: 38



Цитата

мы получаем объект PersistentSet, где метод contains почему-то не работает

А какая версия hibernate?
А то я как-то не задумывался о том, что они могут не работать и так все и сделал у себя как здесь описано (давно уже...).
Взял переопределил методы equals & hashCode и все нормально работает на contains


--------------------
ПРИШЕЛ, УВИДЕЛ - ПЕРЕПИСАЛ...
PM MAIL ICQ   Вверх
magicfly
Дата 28.9.2009, 16:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 109
Регистрация: 21.2.2007

Репутация: 1
Всего: 1



Привет,
 ну во первых можно использовать не обычный итератор а дженерик итератор,
во вторых,  не мешало бы переопределить и хеш код ( когда переопределяешь иквелс лучше сразу и переопределелять хеш код),
в третьих небольшое замечание насчет сравнения лонгов  как было написано
Код

public boolean equals(Object obj) {
            if (obj instanceof Subject) {
                  Subject subj = (Subject)obj;
                  // Value is Long, not long
                  if (subjectId.longValue() == subj.getSubjectId().longValue())
                        return true;
            }
            return false;
}
, так лучше не писать, лучше сделать так:
Код

public boolean equals(Object obj) {
            //here is already checking whether is obj=null
            if (! obj instanceof Subject)  return false;
             Subject subj = (Subject)obj;
            return subjectId==null?obj.getSubjectId()==null:subjectId.equals(obj.getSubjectId());
            
}


Проверил, PersistentSet есть Wrapper вокруг HashSet'a следовательно еще необходимо как было сказано переопределить HashCode


Это сообщение отредактировал(а) magicfly - 28.9.2009, 16:59
PM MAIL   Вверх
Dims
Дата 28.9.2009, 16:44 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1016
Регистрация: 21.11.2006

Репутация: нет
Всего: 11



Вообще-то, чтобы объекты правильно хранились в коллекции, в общем случае, нужно переопределять не только equals, но и hashCode.

Добавлено @ 16:48
Потому что, например, класс HashSet определяет расположение объектов по хэшкоду, то есть, сравнение хэшкодов используется как быстрая (но неточная) операция сравнения. И только когда хэшкоды совпали, при необходимости, вызывается медленная проверка с помощью equals.

Возможно, в ваших функциях тоже где-то в глубине используется хэшкод. Вообще, есть, можно сказать правило: переопределил equals -- переопредели hashCode.

Это сообщение отредактировал(а) Dims - 28.9.2009, 18:36
PM MAIL   Вверх
garbuz
Дата 28.9.2009, 17:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 677
Регистрация: 22.1.2008

Репутация: 8
Всего: 11



Ок, домой приду - поробую переопределить hashCode. А может еще кто скажет как правильно его переопрделять? Мне собственно никогда не приходилось делать это. IDE сама генерит. На нее можно положиться? smile
PM MAIL   Вверх
magicfly
Дата 28.9.2009, 17:56 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 109
Регистрация: 21.2.2007

Репутация: 1
Всего: 1



http://www.geocities.com/technofundo/tech/.../equalhash.html

Смотря как она это делает.  Грубо говоря ты (вы?) можешь использовать те поля по которым идет сравнение и для вычисления hashCode, т.е. равные объекты имеют одинаковый хеш код, разные -разный. Однако на больших массивах данных это не оптимально. Советую посмотреть как работают хеш коллекции для большего понимания. Насколько я помню это есть в "thinking in java"
PM MAIL   Вверх
Dims
Дата 28.9.2009, 18:33 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1016
Регистрация: 21.11.2006

Репутация: нет
Всего: 11



Цитата(garbuz @  28.9.2009,  17:27 Найти цитируемый пост)
А может еще кто скажет как правильно его переопрделять?


Ну я обычно для составного объекта генерю хэшкод так: беру хэшкоды всех членов и произвожу над ними исключающее побитовое "или". 

Если это встроенный тип int, то я ксорю прямо его значение. Если другой, то сперва преобразую в ссылочный и беру его hashCode.

Надо учитывать, что если хэшкод для объекта не переопределён, то это будет нечто вроде адреса в памяти, то есть, число отличающееся у идентичных, но разных объектов (например, у объекта и его клона).

Поэтому может потребоваться переопределить хэшкоды для членов тоже.

До абсурда доводить не надо. На каком-то уровне вполне может подойти стандартная функция от Обжекта.




PM MAIL   Вверх
garbuz
Дата 28.9.2009, 22:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 677
Регистрация: 22.1.2008

Репутация: 8
Всего: 11



Вот что выдала IDEA. Пойдет?
Код

@Override
    public int hashCode() {
        int result = userId.hashCode();
        result = 31 * result + firstName.hashCode();
        result = 31 * result + lastName.hashCode();
        result = 31 * result + login.hashCode();
        result = 31 * result + password.hashCode();
        result = 31 * result + email.hashCode();
        return result;
    }


Проверил - работает! Всем спасибо за помощь  smile 
PM MAIL   Вверх
MisterCleric
Дата 28.9.2009, 22:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1043
Регистрация: 16.2.2006
Где: Харьков, Украина

Репутация: 33
Всего: 38



Galaran, 
Цитата

надо сначала взять значение у этих лонгов, иначе по ссылке будет.

Высказывание не верно: все обертки простых типов + String являются immutable: каждый раз создается новый объект. И говорит о том, что будет сравнение по ссылке не верное. У них как раз методы equals такие, что берутся на сравнеие их значение простых типов. посмотрите исходники


--------------------
ПРИШЕЛ, УВИДЕЛ - ПЕРЕПИСАЛ...
PM MAIL ICQ   Вверх
magicfly
Дата 28.9.2009, 23:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 109
Регистрация: 21.2.2007

Репутация: 1
Всего: 1



Хотелось бы заметить это не совсем правда.
Код

     Long i=Long.parseLong("1");
     Long j=Long.parseLong("1");

вернет одинаковые ссылки т.е. i==j. И для Integer"ов до 128 ссылки тоже будут равны,т.е.
Код

     Integer i=100;
     Integer j=100;
     System.out.println(i==j);
      i=150;
      j=150;
     System.out.println(i==j);

выведет 
true
false
PM MAIL   Вверх
Galaran
Дата 29.9.2009, 00:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 91
Регистрация: 25.5.2009

Репутация: нет
Всего: 2



Цитата(MisterCleric @  28.9.2009,  22:38 Найти цитируемый пост)
Высказывание не верно: все обертки простых типов + String являются immutable: каждый раз создается новый объект. И говорит о том, что будет сравнение по ссылке не верное. У них как раз методы equals такие, что берутся на сравнеие их значение простых типов. посмотрите исходники 


я про это и говорю, что вначале следует взять значения, которые оборачиваются этими объектами и сравнить их


Цитата(magicfly @  28.9.2009,  23:15 Найти цитируемый пост)
вернет одинаковые ссылки т.е. i==j. И для Integer"ов до 128 ссылки тоже будут равны,т.е.


это оптимизация компилятора, верно?

Это сообщение отредактировал(а) Galaran - 29.9.2009, 00:01
PM MAIL   Вверх
MisterCleric
Дата 29.9.2009, 09:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1043
Регистрация: 16.2.2006
Где: Харьков, Украина

Репутация: 33
Всего: 38



Цитата

что вначале следует взять значения, которые оборачиваются этими объектами и сравнить их


А я как раз говорю о том, что не надо брать это значение, если используем метод equals, а не сравниваем их перегруженным для этого оператором "=="
Вот пример метода класса Long

Код

public boolean equals(Object obj) {
    if (obj instanceof Long) {
        return value == ((Long)obj).longValue();
    }
    return false;
    }


Цитата

 System.out.println(i==j);


Мы обсуждаем метод equals, а не оптимизацию интерпретатора для констант по 128


--------------------
ПРИШЕЛ, УВИДЕЛ - ПЕРЕПИСАЛ...
PM MAIL ICQ   Вверх
magicfly
Дата 29.9.2009, 10:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 109
Регистрация: 21.2.2007

Репутация: 1
Всего: 1



Я лишь указал на промахи, в частности на 
Цитата

Высказывание не верно: все обертки простых типов + String являются immutable: каждый раз создается новый объект. И говорит о том, что будет сравнение по ссылке не верное.

Из immutable не следует что каждый раз создается один объект и сравнение по ссылке временами бывает верным
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Java"
LSD   AntonSaburov
powerOn   tux
  • Прежде, чем задать вопрос, прочтите это!
  • Книги по Java собираются здесь.
  • Документация и ресурсы по Java находятся здесь.
  • Используйте теги [code=java][/code] для подсветки кода. Используйтe чекбокс "транслит", если у Вас нет русских шрифтов.
  • Помечайте свой вопрос как решённый, если на него получен ответ. Ссылка "Пометить как решённый" находится над первым постом.
  • Действия модераторов можно обсудить здесь.
  • FAQ раздела лежит здесь.

Если Вам помогли, и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, LSD, AntonSaburov, powerOn, tux.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Java EE (J2EE) и Spring | Следующая тема »


 




[ Время генерации скрипта: 0.0571 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.