为什么我的equals方法不起作用?

时间:2010-07-27 13:58:43

标签: java collections object jpa-2.0

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

失败,报告: 期待:java.util.HashSet< [...所以geht死Legende ......传说有...]>但是:java.util.HashSet< [...所以geht死Legende ......传说有...]>

实际上,通过调试器,我看到两个集合只包含一个含义,objId = 1。 我期望在Meaning类(@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);

那么我的问题可能是什么? Thery都是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 个答案:

答案 0 :(得分:3)

实体的HashCode和equals最好不使用代理ID进行比较,而是使用对象的业务属性或仅使用自然键来实现比较。有关详细信息,请参阅此SO question

编辑:至于为什么这不起作用,我唯一的猜测是你在将对象添加到HashSet后修改对象,特别是更改ID。这将导致contains方法失败,因为它使用hashCode将对象定位为hashmap中的键,如果ID已更改,它将查找底层hashMap中的错误位置。

答案 1 :(得分:1)

如果

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)“a”包含来自“b”的所有元素。

你似乎失败了最后一个。实际上,由于在HashSet上对containsAll执行自身操作失败,因此必须成为意义上的equals方法。阅读集合上的containsAllcontains方法的代码(Java 6)清楚地表明hashCode方法不用于此目的。

答案 2 :(得分:1)

你的对象是整数还是长整数?那你的问题可能与equals方法中的autoboxing有关:

objId == other.objId

这将适用于第一次测试中的小常量,因为它们是缓存的。通常,在这种情况下,您应该使用equals方法。 equals方法中的那一行最好写成:

 return objId == null ? this == other : objId.equals(other.objId);

答案 3 :(得分:0)

如果你用objId进行散列,那么它不应该是可变的(正如setObjId建议的那样)。特别是,如果您坚持使用该方法,则不应在将对象放入哈希集后调用它。

答案 4 :(得分:0)

您正在使用hibernate实体,因此您不应直接引用成员变量,因为它们可能会从数据库中延迟加载。所以,你的equals / hashCode方法应该使用getObjId()而不是objId。

另外,如另一篇文章所述,如果你的objId是一个对象类型(Integer,Long),你不应该使用“==”。

答案 5 :(得分:-1)

问题是containsAll比较对象的引用(使用==)而不是调用.equals。根据文档,HashSet.equals在内部使用containsAll。

一种可能的解决方案是从HashSet派生自己的类并覆盖containsAll方法。