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

发布于 2024-09-11 18:48:38 字数 3593 浏览 5 评论 0原文

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

失败,报告: 预期: java.util.HashSet<[...so geht die Legende ... ...传说有它...]>但是: java.util.HashSet<[...so geht die 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);

那么我的问题可能是什么? 它们都是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);
    }
assertEquals(def.getMengs(), exp.getMengs());

fails, reporting:
expected: java.util.HashSet<[...so geht die Legende ... ...legend has it ... ]> but was: java.util.HashSet<[...so geht die Legende ... ...legend has it ... ]>

Indeed, through the debugger I see that both sets contain only one Meaning with objId = 1 for both.
I expected the following code in Meaning class (@Entity) to guarantee that the above works.

@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;
}

Indeed, this test passes:

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

So what could be my problem?
Thery are both HashSets, they are both of the same size, and the objects inside them equals. Indeed

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

before it passes. However this won't (but I don't know why):

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

So, it's the problem.

Here's the test code:

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 just wraps em.find. InsertWords should be persisting def and 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);
    }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

荒路情人 2024-09-18 18:48:40

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

If you hash by objId, then it shouldn't be mutable (as the method setObjId suggests). In particular, if you insist on having that method, you shouldn't call it after putting an object in a hashset.

海拔太高太耀眼 2024-09-18 18:48:40

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

一个可能的解决方案是从 HashSet 派生您自己的类并重写 containsAll 方法。

The problem is that containsAll compares references to the objects (using ==) instead of calling .equals. HashSet.equals uses containsAll internally according to the documentation.

A possible solution is to derive your own class from HashSet and override the containsAll method.

清君侧 2024-09-18 18:48:39

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

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

HashCode and equals for entities are best written without using the surrogate ID for comparison, but rather implement comparison using the business attributes of the object or just the natural keys. See this SO question for details.

EDIT: As to why this doesn't work, my only guess is that you are modifying the object after adding it to the HashSet, in particular changing the ID. This will cause the contains method to fail, since it uses the hashCode to locate the object as a key in the hashmap, and if the ID has changed, it will be looking in the wrong place in the underlying hashMap.

花想c 2024-09-18 18:48:39

如果

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 本身会失败,因此它必须是 Meaning 上的 equals 方法。阅读 Set 上的 containsAllcontains 方法的代码 (Java 6) 清楚地表明 hashCode 方法不用于此目的。

If

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

passes, then the most likely explanation is that def.getMengs() contains all elements from exp.getMengs() plus some other not in the latter.

Try reversing it, i.e.

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

or simply

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

Edit

I see I've misread your question. However, this does clarify the situation. The equals method checks for 3 things. 1) That both are of type Set. 2) Same size and 3) That "a" contains all elements from "b".

You seem to be failing that last one. Indeed, since doing containsAll on a HashSet with itself is failing it must be the equals method on Meaning. Reading the code (Java 6) for the containsAll and contains methods on Sets clearly shows that the hashCode method is not used for this purpose.

可爱暴击 2024-09-18 18:48:39

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

objId == other.objId

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

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

Is your objId an Integer or Long? Then your problem is probably related to autoboxing in the equals method:

objId == other.objId

This will work for small constants like in your first test since these are cached. Generally you should use the equals method in this case. That line in the equals method could better be written as:

 return objId == null ? this == other : objId.equals(other.objId);
柠檬色的秋千 2024-09-18 18:48:39

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

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

You are using hibernate entities, so you should never refer to member variables directly, as they may be lazily loaded from the database. so, your equals/hashCode method should use getObjId() instead of objId.

also, as mentioned in another post, if your objId is an object type (Integer, Long), you should not be using "==".

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文