2010-07-27 77 views
1
assertEquals(def.getMengs(), exp.getMengs()); 

失敗,報告: 預期:java.util.HashSet中< [...所以geht模具的Légende......傳說...]>卻被:java.util.HashSet中< [ ...所以geht死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); 

那麼,什麼可能是我的問題嗎? 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); 
    } 
+0

'int hash = 7;哈希= 67 *哈希'?爲什麼不'int hash = 469 + obj.Id'?或者只是'返回obj.Id'?向散列碼添加常數是不必要的, – NullUserException 2010-07-27 14:03:34

+1

@NullUserException:'return 4;'是正確的變體! – Roman 2010-07-27 14:05:08

回答

0

您正在使用Hibernate的實體,所以你永遠不應該是指成員變量直接,因爲他們可以從數據庫延遲加載。所以,你的equals/hashCode方法應該使用getObjId()而不是objId。

另外,正如另一篇文章中提到的,如果你的objId是一個對象類型(Integer,Long),你不應該使用「==」。

-1

問題是containsAll比較對象的引用(使用==)而不是調用.equals。根據文檔,HashSet.equals在內部使用containsAll。

可能的解決方案是從HashSet派生自己的類並重寫containsAll方法。

+3

-1'.containsAll()'使用'.equals()'進行比較 – NullUserException 2010-07-27 14:11:36

+0

@NullUserException,那麼問題在哪裏?你說這個常數是不必要的,但這不是問題。 – simpatico 2010-07-27 14:13:56

+0

@simpatico是你的'.equals()'工作嗎? – NullUserException 2010-07-27 14:17:58

0

如果你通過objId進行散列,那麼它不應該是可變的(如setObjId方法所建議的那樣)。特別是,如果你堅持使用這種方法,你不應該在把一個對象放入哈希集中之後調用它。

+0

嗯..我添加了setObjId方法只是爲了測試,現在我遇到了問題。我同意它不應該是可變的。 但是你根本沒有回答這個問題,對嗎? – simpatico 2010-07-27 14:16:23

+1

*如果你把它放在一個HashSet(我不知道你是否因爲沒有包含所有的代碼)而改變objId,*那麼*這就是問題所以我回答這個問題, 有條件的 :) )。 – 2010-07-27 14:21:08

+0

我從不改變objId,除了那個測試代碼(這不是問題的一部分)。 – simpatico 2010-07-27 14:26:29

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方法。閱讀代碼(Java 6)containsAllcontains集合上的方法清楚地表明hashCode方法不用於此目的。

+0

不,它不。調試器顯示它們具有相同的大小,並且尺寸斷言通過。 – simpatico 2010-07-27 14:19:25

+0

有趣的是,這也失敗了:assertTrue(def.getMengs()。containsAll(def.getMengs())); – simpatico 2010-07-27 14:27:16

3

對於實體的HashCode和equals,最好是不使用代理ID進行比較,而是使用對象的業務屬性或僅使用自然鍵來實現比較。有關詳細信息,請參閱此SO question

編輯:至於爲什麼這不起作用,我唯一的猜測是你將它添加到HashSet後修改對象,特別是改變ID。這將導致contains方法失敗,因爲它使用hashCode將對象定位爲hashmap中的鍵,並且如果ID已更改,它將查找底層hashMap中的錯誤位置。

+0

你不回答這個問題。 – simpatico 2010-07-27 14:22:22

+0

@simpatico - 我正在接近它 - 檢查JDK源代碼。請參閱我的編輯。 – mdma 2010-07-27 14:25:01

+0

我看不到如何改變對象(除了hashcode或objId)導致問題。我會發布測試代碼。 – simpatico 2010-07-27 14:31:08

1

你的objId是整數還是長整數?然後,您的問題可能與在equals方法自動裝箱:

objId == other.objId 

這將在你的第一個測試小常數一樣工作,因爲這些被緩存。通常你應該在這種情況下使用equals方法。 equals方法中該行可以更好地被寫爲:

return objId == null ? this == other : objId.equals(other.objId); 
相關問題