Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Java EE (J2EE) и Spring > Проверить объекты на equals


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

            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. Может в них дело?
Спасибо.

Автор: Galaran 26.9.2009, 08:18
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;
}

Автор: garbuz 26.9.2009, 17:24
Парсер лохе! Первый раз не отправил мое сообщение smile

Galaran, спасибо за совет, так и думал сделать, однако вчера на это не хватило сил (5-ый час). Сделал сегодня - не работает. Дело в том, что при получении дочерней коллекции из объекта, вытащенного с пом хибернейт, мы получаем объект PersistentSet, где метод contains почему-то не работает. На это есть даже баг в джире http://opensource.atlassian.com/projects/hibernate/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

Автор: MisterCleric 28.9.2009, 15:01
Цитата

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

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

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

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

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

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

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

Автор: garbuz 28.9.2009, 17:27
Ок, домой приду - поробую переопределить hashCode. А может еще кто скажет как правильно его переопрделять? Мне собственно никогда не приходилось делать это. IDE сама генерит. На нее можно положиться? smile

Автор: magicfly 28.9.2009, 17:56
http://www.geocities.com/technofundo/tech/java/equalhash.html

Смотря как она это делает.  Грубо говоря ты (вы?) можешь использовать те поля по которым идет сравнение и для вычисления hashCode, т.е. равные объекты имеют одинаковый хеш код, разные -разный. Однако на больших массивах данных это не оптимально. Советую посмотреть как работают хеш коллекции для большего понимания. Насколько я помню это есть в "thinking in java"

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


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

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

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

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

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




Автор: garbuz 28.9.2009, 22:24
Вот что выдала 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 

Автор: MisterCleric 28.9.2009, 22:38
Galaran, 
Цитата

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

Высказывание не верно: все обертки простых типов + String являются http://www.google.com.ua/search?q=java+immutable: каждый раз создается новый объект. И говорит о том, что будет сравнение по ссылке не верное. У них как раз методы equals такие, что берутся на сравнеие их значение простых типов. посмотрите исходники

Автор: magicfly 28.9.2009, 23:15
Хотелось бы заметить это не совсем правда.
Код

     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

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


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


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


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

Автор: MisterCleric 29.9.2009, 09:16
Цитата

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


А я как раз говорю о том, что не надо брать это значение, если используем метод 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

Автор: magicfly 29.9.2009, 10:16
Я лишь указал на промахи, в частности на 
Цитата

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

Из immutable не следует что каждый раз создается один объект и сравнение по ссылке временами бывает верным

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)