Почему мой метод равных не сработает? - PullRequest
1 голос
/ 27 июля 2010
assertEquals(def.getMengs(), exp.getMengs());

завершается неудачно, сообщается: ожидается: java.util.HashSet <[... so geht die Legende ... ... у легенды есть это ...]> но было: java.util.HashSet <[... так что, умри, Легенд ... ... легенда имеет это ...]>

Действительно, через отладчик я вижу, что оба набора содержат только одно значение с objId = 1 для обоих.Я ожидал, что следующий код в классе смысла (@Entity) будет гарантировать, что вышеприведенный код работает.

@Override
public boolean equals(Object object) {
   if (!(object instanceof Meaning)) {
        return false;
   }
   Meaning other = (Meaning) object;
   if (other != null && objId == other.objId) return true;
   return false;
}

@Override
public int hashCode() {
    int hash = 7;
    hash = 67 * hash + this.objId;
    return hash;
}

Действительно, этот тест проходит:

 db.insert(admin);
    final Meaning meng = new Meaning(admin, new Expression("essen"));
    meng.setObjId(11);
    final Meaning meng1  = new Meaning(admin, new Expression("mangiare"));
    meng1.setObjId(11);
    assertEquals(meng,meng1);

Так в чем может быть моя проблема?Они оба являются HashSets, оба имеют одинаковый размер, и объекты внутри них равны.Действительно

            assertEquals(def.getMengs().iterator().next(), exp.getMengs().iterator().next());

до того, как это пройдет.Однако это не будет (но я не знаю почему):

assertTrue(def.getMengs().containsAll(exp.getMengs()));

Итак, проблема в этом.

Вот тестовый код:

try{
            db.insertWords(toEnumMap(mengs[i],admin));
        }catch(Exception e){
            fail(e.getMessage());
        }
        final Expression exp = db.get(Expression.class, mengs[i][0]);
        testGender(exp, mengs[i][2]);

        final Expression def = db.get(Expression.class, mengs[i][1]);
        assertNotNull(def);

        assertEquals(def.getMengs().iterator().next(), exp.getMengs().iterator().next());
        assertEquals(exp.getMengs().size(), def.getMengs().size());
        assertTrue(def.getMengs().containsAll(def.getMengs()));
        assertTrue(def.getMengs().containsAll(exp.getMengs()));
        assertEquals(def.getMengs(), exp.getMengs());

db.get просто оборачивает em.find.InsertWords должен сохранять def и exp.

public void insertWords(EnumMap<Input, MemoEntity> input) throws MultipleMengsException {
    insert(input.get(Input.expression)); //INSERT OR IGNORE
    final boolean isNewDef = insert(input.get(Input.definition));

    final Expression def = get(Expression.class, input.get(Input.definition).getId());
    final Expression exp = get(Expression.class, input.get(Input.expression).getId());
    final MUser usr = get(MUser.class, input.get(Input.user).getId());

    final Set<Meaning> mengs = getMengs(usr,def,isNewDef);
    if (mengs == null) {//is new to the user
        final Meaning meng = new Meaning(usr, exp, def);
        insert(meng);

    } else { //old meaning
        if (mengs.size() > 1) throw new MultipleMengsException(mengs);
        else{
            final Meaning meng = mengs.iterator().next();
            meng.addExp(exp);
            meng.setLastPublishedDate(null); //reschedule
        }
    }
    Logger.getAnonymousLogger().log(Level.INFO, "inserted pair <{0},{1}>", new String[]{exp.getExpression(), def.getExpression()});
}

public boolean insert(final MemoEntity entity) {
        if (em.find(entity.getClass(), entity.getId()) == null) {
            et.begin();
            em.persist(entity);
            et.commit();
            return true;
        }
        return false;
    }

public <MemoEntity> MemoEntity get(final Class<MemoEntity> entityClass, final Object primaryKey) {
        return em.find(entityClass, primaryKey);
    }

Ответы [ 6 ]

3 голосов
/ 27 июля 2010

HashCode и equals для сущностей лучше всего писать без использования суррогатного идентификатора для сравнения, а скорее для осуществления сравнения с использованием бизнес-атрибутов объекта или только естественных ключей. См. Этот ТАК вопрос для деталей.

РЕДАКТИРОВАТЬ: Что касается того, почему это не работает, я могу только предположить, что вы модифицируете объект после добавления его в HashSet, в частности, меняя идентификатор. Это приведет к сбою метода contains, поскольку он использует hashCode для определения местоположения объекта в качестве ключа в hashmap, и, если идентификатор изменился, он будет искать неправильное место в базовом hashMap.

1 голос
/ 27 июля 2010

Ваш объект является целым или длинным?Тогда ваша проблема, вероятно, связана с автобоксом в методе equals:

objId == other.objId

Это будет работать для небольших констант, как в вашем первом тесте, поскольку они кэшируютсяОбычно в этом случае вы должны использовать метод equals.Эту строку в методе equals лучше записать как:

 return objId == null ? this == other : objId.equals(other.objId);
1 голос
/ 27 июля 2010

Если

assertTrue(def.getMengs().containsAll(exp.getMengs()));

пройдено, то наиболее вероятное объяснение состоит в том, что def.getMengs() содержит все элементы из exp.getMengs() плюс , некоторые другие не в последнем.

Попробуйте поменять его, то есть

assertTrue(exp.getMengs().containsAll(def.getMengs()));

или просто

assertEqual(exp.getMengs().size(), def.getMengs().size());

Изменить

Я понял, что неправильно понял ваш вопрос.Однако это проясняет ситуацию.Метод equals проверяет 3 вещи.1) Оба имеют тип Set.2) Тот же размер и 3) что «а» содержит все элементы из «б».

Вы, похоже, проваливаете это последнее.Действительно, поскольку выполнение containsAll для HashSet с самим собой приводит к сбою, должен быть методом equals в Значение.Чтение кода (Java 6) для методов containsAll и contains на множествах ясно показывает, что метод hashCode не используется для этой цели.

0 голосов
/ 27 июля 2010

Вы используете объекты гибернации, поэтому никогда не следует ссылаться на переменные-члены напрямую, поскольку они могут быть лениво загружены из базы данных.Итак, ваш метод equals / hashCode должен использовать getObjId () вместо objId.

также, как упоминалось в другом посте, если ваш objId является типом объекта (Integer, Long), вы не должны использовать "==».

0 голосов
/ 27 июля 2010

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

0 голосов
/ 27 июля 2010

Проблема в том, что containsAll сравнивает ссылки на объекты (используя ==) вместо вызова .equals.HashSet.

...