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

失败,报告:预期:java.util.HashSet<[...so geht die Legende ... ...legend has it ...]> 但是:java.util.HashSet<[...so geht die Legende ... ...传说有它... ]>

实际上,通过调试器,我看到两个集合都只包含一个含义,其中 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);

那么我的问题可能是什么?它们都是 HashSet,它们的大小相同,并且它们内部的对象相等。确实

            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);
    }
4

6 回答 6

3

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

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

于 2010-07-27T14:18:11.253 回答
1

如果

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

通过,那么最可能的解释是def.getMengs()包含所有元素 fromexp.getMengs() 加上一些其他不在后者中的元素。

尝试反转它,即

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

或者干脆

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

编辑

我知道我误读了你的问题。但是,这确实澄清了情况。equals 方法检查 3 件事。1)两者都是类型Set。2) 相同大小和 3) “a”包含“b”中的所有元素。

你似乎没有通过最后一个。实际上,由于containsAll对自身进行 HashSet 操作失败,因此它必须equals有关含义的方法。containsAll阅读 Sets 上的and方法的代码(Java 6)contains清楚地表明该hashCode方法没有用于此目的。

于 2010-07-27T14:11:00.687 回答
1

您的 objId 是整数还是长整数?那么您的问题可能与 equals 方法中的自动装箱有关:

objId == other.objId

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

 return objId == null ? this == other : objId.equals(other.objId);
于 2010-07-27T14:53:38.737 回答
0

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

于 2010-07-27T14:08:26.047 回答
0

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

另外,正如另一篇文章中提到的,如果您的 objId 是对象类型(整数、长整数),则不应使用“==”。

于 2010-07-27T16:43:32.593 回答
-1

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

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

于 2010-07-27T14:07:53.583 回答