2011-09-27 25 views
4

我的应用程序出现了一个奇怪的问题,我会尽快解释全局体系结构,然后深入解答我的问题。Java HashMap没有找到关键字,但应该是

我使用服务来填充来自我的数据库(JPA驱动)的HashMap<DomainObject,Boolean>,然后通过EJB远程方法调用(使用Apache Wicket)返回到我的视图。在这部分中,我将新的DomainObject添加到返回的映射中,以便存储来自最终用户的任何新值。

当用户在其浏览器中点击“添加”按钮时,会出现问题,我尝试在地图中检索新创建的项目,但失败。通过使用调试器,我面对以下事情。

假设HashMap<DomainObject, Boolean> mapDomainObject do是两个变量有趣我在调试器的结果如下

map.keySet();给我对应于do(即使@whatever simili基准是相同的),hashcode()在两个对象返回的对象类似值和两个回报之间equals()true

map.containsKey(do);返回false

map.get(do);返回null,奇怪,因为我的钥匙似乎在map

假设我的新建项目是keySet()列举的第一个关键,我做了以下内容: map.get(new ArrayList(map.keySet()).get(0)),并返回null。

如果它可以帮助,通过附加断点我DomainObject.equals()DomainObject.hashcode()方法,我发现map.get()只调用hashcode()而不是equals()

我发现的唯一解决方法是在现有的地图new HashMap(map)之上重新创建新地图,在这张新地图中,我根本没有任何问题通过其关键字查找对象。

我希望这里有人可以给我一个指示什么发生,谢谢。

环境中使用:

  • 的Sun Java在OS X 10.7.1
  • 的OpenJDK 1.6.0_18 64 1.6.0_26 64位的Debian 6.0.2下(2.6.32)
  • 阿帕奇检票1.4.17
  • Oracle Glassfish 3.1.1
  • JBoss Hibernate 3.6。5

DomainObject代码:

public class AssetComponentDetailTemplate extends BaseEntite<Long> { 
public enum DataType { 
    TXT, 
    DATE, 
    INT, 
    JOIN, 
    LIST, 
    COULEURS, 
    REFERENCE 
} 

public enum Tab { 
    IDENTITE, 
    LOCALISATION, 
    CYCLE_DE_VIE, 
    FINANCE, 
    RESEAU, 
    DETAIL 
} 

@Id 
@GeneratedValue(strategy = GenerationType.AUTO) 
private Long id; 
@Column(nullable = false) 
private String name; 
@Column(nullable = false) 
@Enumerated(EnumType.STRING) 
private DataType dataType; 
private Integer classNameId; 
private Long orderId; 
private Long nextAssetComponentDetailTemplateId; 
private String unit; 
@Enumerated(EnumType.STRING) 
private Tab tab; 

@Column(nullable = false) 
private Long uniqueOrganizationId; 

@OneToMany(fetch = FetchType.LAZY) 
@JoinColumn(name = "idAssetComponentDetailTemplate", insertable = false, updatable = false) 
private List<AssetComponentDetailJoin> assetComponentDetailJoins; 

private Boolean mandatory = false; 

public AssetComponentDetailTemplate() { 
} 

public Long getId() { 
    return id; 
} 

public void setId(final Long id) { 
    this.id = id; 
} 

public String getName() { 
    return name; 
} 

public void setName(final String name) { 
    this.name = name; 
} 

public DataType getDataType() { 
    return dataType; 
} 

public void setDataType(final DataType dataType) { 
    this.dataType = dataType; 
} 

public Integer getClassNameId() { 
    return classNameId; 
} 

public void setClassNameId(final Integer classNameId) { 
    this.classNameId = classNameId; 
} 

public Long getUniqueOrganizationId() { 
    return uniqueOrganizationId; 
} 

public void setUniqueOrganizationId(final Long uniqueOrganizationId) { 
    this.uniqueOrganizationId = uniqueOrganizationId; 
} 

public Long getNextAssetComponentDetailTemplateId() { 
    return nextAssetComponentDetailTemplateId; 
} 

public void setNextAssetComponentDetailTemplateId(final Long nextAssetComponentDetailTemplateId) { 
    this.nextAssetComponentDetailTemplateId = nextAssetComponentDetailTemplateId; 
} 

public String getUnit() { 
    return unit; 
} 

public void setUnit(final String unit) { 
    this.unit = unit; 
} 

public Tab getTab() { 
    return tab; 
} 

public void setTab(final Tab tab) { 
    this.tab = tab; 
} 

public Long getOrder() { 
    return orderId; 
} 

public void setOrder(final Long order) { 
    this.orderId = order; 
} 

public Boolean isMandatory() { 
    return mandatory; 
} 

@Override 
public String toString() { 
    return name; 
} 

@Override 
public boolean equals(final Object o) { 
    if (this == o) { 
     return true; 
    } 
    if (o == null || getClass() != o.getClass()) { 
     return false; 
    } 

    final AssetComponentDetailTemplate that = (AssetComponentDetailTemplate) o; 

    if (classNameId != null ? !classNameId.equals(that.classNameId) : that.classNameId != null) { 
     return false; 
    } 
    if (dataType != that.dataType) { 
     return false; 
    } 
    if (id != null ? !id.equals(that.id) : that.id != null) { 
     return false; 
    } 
    if (name != null ? !name.equals(that.name) : that.name != null) { 
     return false; 
    } 
    if (nextAssetComponentDetailTemplateId != null ? 
     !nextAssetComponentDetailTemplateId.equals(that.nextAssetComponentDetailTemplateId) : 
     that.nextAssetComponentDetailTemplateId != null) { 
     return false; 
    } 
    if (orderId != null ? !orderId.equals(that.orderId) : that.orderId != null) { 
     return false; 
    } 
    if (tab != that.tab) { 
     return false; 
    } 
    if (uniqueOrganizationId != null ? !uniqueOrganizationId.equals(that.uniqueOrganizationId) : 
     that.uniqueOrganizationId != null) { 
     return false; 
    } 
    if (unit != null ? !unit.equals(that.unit) : that.unit != null) { 
     return false; 
    } 

    return true; 
} 

@Override 
public int hashCode() { 
    int result = id != null ? id.hashCode() : 0; 
    result = 31 * result + (name != null ? name.hashCode() : 0); 
    result = 31 * result + (dataType != null ? dataType.hashCode() : 0); 
    result = 31 * result + (classNameId != null ? classNameId.hashCode() : 0); 
    result = 31 * result + (orderId != null ? orderId.hashCode() : 0); 
    result = 31 * result + 
      (nextAssetComponentDetailTemplateId != null ? nextAssetComponentDetailTemplateId.hashCode() : 0); 
    result = 31 * result + (unit != null ? unit.hashCode() : 0); 
    result = 31 * result + (tab != null ? tab.hashCode() : 0); 
    result = 31 * result + (uniqueOrganizationId != null ? uniqueOrganizationId.hashCode() : 0); 
    return result; 
} 
+2

您应该显示一些代码。至少你如何管理地图以及“equals”和“hashCode”的实现。 – home

+0

我们可以看一些代码吗? – user482594

+0

恐怕提取代码不会很容易(它在应用程序中紧密耦合)。我添加了'DomainObject'代码。 –

回答

4

[这基本上扩展了加斯帕的答案,但细节可能会帮助你]

由于使用new HashMap(map)是目前能找到的元素,我怀疑该domainObject的的hashCode()改变它添加到地图后重新创建地图。

例如,如果你domainObject的外观下面

class DomainObject { 
    public String name; 
    long hashCode() { return name.hashCode(); } 
    boolean equals(Object other) { /* compare name in the two */' 
} 

然后

Map<DomainObject, Boolean> m = new HashMap<DomainObject, Boolean>(); 
    DomainObject do = new DomainObject(); 
    do.name = "ABC"; 
    m.put(do, true); // do goes in the map with hashCode of ABC 
    do.name = "DEF"; 
    m.get(do); 

最后一条语句以上将返回null。因为你在地图中的do对象在"ABC".hashCode()的桶下; "DEF".hashCode()桶中没有任何内容。

地图中对象的hashCode在添加到地图后不应更改。确保hashCode所依赖的字段的最佳方式必须是不可变的

+0

这是一个有趣的指针,我会继续看... –

3

这是您的线索:

hashCode()方法在两个对象返回值类似

对于对象是被认为是相同的,他们的哈希码不应该只是相似的,他们必须完全相同。

如果两个对象有不同的哈希码,那么就容器而言,对象是不同的。没有必要拨打equals()

Javadoc

hashCode常规协定是:

  • 如果两个对象相等根据equals(Object)方法,然后 调用每个两个对象的hashCode方法必须产生 相同的整数结果。

如果我是你,我会仔细看在DomainObject.hashcode()DomainObject.equals(),看看是什么导致被打破的合同。

+0

当我的意思相同时,我的意思是等值。我发现令人惊讶的事情是,它从我的服务层来的对象按预期工作,但不能创建一个视图。这是我认为广泛使用的一种情况。如果我的'hashcode()'是错误的,使用旧的创建新的Map也不行,对吧? –

3

您的DomainObject类是不可变的吗?它是否正确实施了hashCodeequals方法?

注意,你会惹上麻烦,如果你DomainObject类并不是一成不变的,你改变对象的状态,而这是在某种程度上将改变调用hashCodeequals的结果的地图。

hashCode必须以这样的方式实现,即当比较这些对象时,只要equals返回true,就会返回两个对象的相同值。有关详细信息,请参阅java.lang.Object.hashCode()的API文档。

0

map.get(do)返回null可以通过假设该密钥的Boolean值可能是nullmap.containsKey(do)返回false需要dohashCode是在调用containsKey(do)给它的时间的不同很容易解释的hashCode在检索时它从keySet

要看到发生了什么,你可以(暂时)使用更详细的实施HashMap中的... 也许是这样的:

public class VerboseHashMap<K, V> implements Map<K, V> { 

    private transient final static Logger logger = Logger.getLogger(VerboseHashMap.class); 
    private HashMap<K, V> internalMap = new HashMap<K, V>(); 

    public boolean containsKey(Object o) { 
     logger.debug("Object HashCode: " + o.hashCode()); 
     logger.debug("Map contents:"); 
     for (Entry<K, V> entry : internalMap.entrySet()) { 
      logger.debug(entry.getKey().hashCode() + " - " + entry.getValue().toString()); 
     } 
     return internalMap.containsKey(o); 
    } 

    public V get(Object key) { 
     logger.debug("Object HashCode: " + key.hashCode()); 
     logger.debug("Map contents:"); 
     for (Entry<K, V> entry : internalMap.entrySet()) { 
      logger.debug(entry.getKey().hashCode() + " - " + entry.getValue().toString()); 
     } 
     return internalMap.get(key); 
    } 

} 

你需要地图的所有其他要求地图也是你的内部映射的接口。

注:此代码不能用于生产,也不是面向任何方式的性能,美观大方或unsmelly ....

第2注(看到你的代码后):要使用你的域对象一个关键的hashMap,你应该只使用你的对象的不可变部分hashCode和等于(在这种情况下,id值)。否则,延迟加载其它值会改变的hashCode ...

在回答您的评论:

public class Demo extends TestCase { 

public void testMap() { 

    Map<DomainObject, String> map = new HashMap<DomainObject, String>(); 
    DomainObject sb = new DomainObject(); 
    map.put(sb, "Some value"); 
    System.out.println(map.containsKey(sb)); 
    sb.value = "Some Text"; 
    System.out.println(map.containsKey(sb)); 

} 

    private static class DomainObject { 

     public String value = null; 

     @Override 
     public int hashCode() { 
      final int prime = 31; 
      int result = 1; 
      result = prime * result + ((value == null) ? 0 : value.hashCode()); 
      return result; 
     } 

     @Override 
     public boolean equals(Object obj) { 
      if (this == obj) 
       return true; 
      if (obj == null) 
       return false; 
      if (getClass() != obj.getClass()) 
       return false; 
      DomainObject other = (DomainObject) obj; 
      if (value == null) { 
       if (other.value != null) 
        return false; 
      } else if (!value.equals(other.value)) 
       return false; 
      return true; 
     } 

    } 

} 

打印

true 
false 

为重点的哈希码计算在投入的时间它进入地图。

+0

如果我在'do'和'do'对应的键上同时计算'hashCode()',我会得到完全相同的结果,所以我会说它不是我的'do'对象中的一个改变的元素(因为它引用了同一个对象)。在我的情况下,我的映射中的右值不是'true'或'false',从不'null',调试器显示的值是'false'。 –

+0

查看我的上次编辑 – Nicktar

+0

谢谢,我现在明白发生了什么 –