Java HashMap 没有找到键,但它应该
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7565341/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Java HashMap not finding key, but it should
提问by Cedric Gatay
I have a strange issue occuring in my application, I will quickly explain global architecture and then my problem in depth.
我的应用程序中出现了一个奇怪的问题,我将快速解释全局架构,然后深入解释我的问题。
I use a service to populate a HashMap<DomainObject,Boolean>
coming from my database (JPA driven) which is in turn returned to my view, via an EJB remote method call (using Apache Wicket). In this part, I add a new DomainObject
to the map returned in order to store any new value from my end user.
我使用一个服务来填充HashMap<DomainObject,Boolean>
来自我的数据库(JPA 驱动)的数据,然后通过 EJB 远程方法调用(使用 Apache Wicket)返回到我的视图。在这部分中,我DomainObject
向返回的地图添加了一个 new以存储来自最终用户的任何新值。
The problem occurs when the user hit the "add" button in its browser, I try to retrieve the newly created item in my map, but it fails. By playing with the debugger I face the following things.
当用户点击浏览器中的“添加”按钮时会出现问题,我尝试在我的地图中检索新创建的项目,但失败了。通过使用调试器,我面临以下问题。
Assuming HashMap<DomainObject, Boolean> map
and DomainObject do
are the two variables interesting I have the following results in the debugger
假设HashMap<DomainObject, Boolean> map
和DomainObject do
是两个变量有趣我在调试器下面的结果
map.keySet();
gives me an object corresponding to do
(even the @whatever simili-reference is identical), hashcode()
on both objects returns similar value and equals()
between the two returns true
map.keySet();
给我一个对应的对象do
(即使@whatever simili-reference 是相同的),hashcode()
在两个对象上返回相似的值,并且equals()
在两个返回之间true
map.containsKey(do);
returns false
map.containsKey(do);
回报 false
map.get(do)
; returns null
, weird because my key seems to be in the map
.
map.get(do)
; 返回null
,很奇怪,因为我的密钥似乎在map
.
Assuming my newly created item is the first key enumerated by keySet()
, I do the following :
map.get(new ArrayList(map.keySet()).get(0))
, and it returns null.
假设我新创建的项目是由 枚举的第一个键keySet()
,我执行以下操作 :
map.get(new ArrayList(map.keySet()).get(0))
,它返回 null。
If it can help, by attaching breakpoints to my DomainObject.equals()
and DomainObject.hashcode()
methods I found that map.get()
is only calling hashcode()
and not equals()
.
如果它可以提供帮助,通过将断点附加到 myDomainObject.equals()
和DomainObject.hashcode()
方法,我发现它map.get()
只是调用hashcode()
而不是equals()
.
The only workaround I found is to recreate a new map on top of the existing one new HashMap(map)
, in this new map, I have no problem at all looking up an object by its key.
我发现的唯一解决方法是在现有地图的顶部重新创建一个新地图new HashMap(map)
,在这张新地图中,我完全没有问题通过其键查找对象。
I hope someone here can give my a pointer on what happens, thanks.
我希望这里有人可以给我指点一下会发生什么,谢谢。
Environment used :
- Sun Java 1.6.0_26 x64 under OS X 10.7.1
- OpenJDK 1.6.0_18 x64 under Debian 6.0.2 (2.6.32)
- Apache Wicket 1.4.17
- Oracle Glassfish 3.1.1
- JBoss Hibernate 3.6.5
使用环境:
- OS X 10.7.1 下的 Sun Java 1.6.0_26 x64
- Debian 6.0.2 (2.6.32) 下的 OpenJDK 1.6.0_18 x64
- Apache Wicket 1.4.17
- 甲骨文 Glassfish 3.1.1
- JBoss 休眠 3.6.5
DomainObject
code :
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;
}
采纳答案by Miserable Variable
[This basically expands on Jesper's answer but the details may help you]
[这基本上扩展了 Jesper 的答案,但细节可能对您有所帮助]
Since recreating the map using new HashMap(map)
is able to find the element I am suspecting that the hashCode()
of the DomainObject changed after adding it to the Map.
由于使用重新创建地图new HashMap(map)
能够找到元素,我怀疑hashCode()
在将其添加到地图后 DomainObject 的 发生了变化。
For example if your DomainObject looks the following
例如,如果您的 DomainObject 如下所示
class DomainObject {
public String name;
long hashCode() { return name.hashCode(); }
boolean equals(Object other) { /* compare name in the two */'
}
Then
然后
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);
The last statement above will return null
. Because the do
object you have inside the map is under the bucket of "ABC".hashCode()
; there is nothing in the "DEF".hashCode()
bucket.
上面的最后一条语句将返回null
。因为do
你在地图里面的对象在"ABC".hashCode()
; "DEF".hashCode()
桶里什么都没有。
The hashCode of the Objects in map should not change once added to map. The best way to ensure it is that the fields on which hashCode depends must be immutable.
map 中对象的 hashCode 一旦添加到 map 中就不应更改。确保它的最佳方法是 hashCode 所依赖的字段必须是不可变的。
回答by NPE
Here is your clue:
这是你的线索:
hashcode() on both objects returns similar value
两个对象上的 hashcode() 返回相似的值
For the objects to be considered equal, their hash codes shouldn't just be similar, they must be identical.
对于被视为相等的对象,它们的哈希码不应该只是相似,它们必须相同。
If two objects have different hash codes, then as far as the container is concerned the objects are different. There's no need to even call equals()
.
如果两个对象具有不同的哈希码,那么就容器而言,对象是不同的。甚至不需要打电话equals()
。
From the Javadoc:
从Javadoc:
The general contract of
hashCode
is:
- If two objects are equal according to the
equals(Object)
method, then calling thehashCode
method on each of the two objects must produce the same integer result.
总合同
hashCode
为:
- 如果两个对象根据
equals(Object)
方法相等,则hashCode
对两个对象中的每一个调用该方法必须产生相同的整数结果。
If I were you, I'd take a close look at DomainObject.hashcode()
and DomainObject.equals()
to see what's causing the contract to be broken.
如果我是你,我会拿近看DomainObject.hashcode()
和DomainObject.equals()
看是什么导致被打破的合同。
回答by Jesper
Is your DomainObject
class immutable? Does it have properly implemented hashCode
and equals
methods?
你的DomainObject
类是不可变的吗?是否有适当的实施hashCode
和equals
方法?
Note that you will get into trouble if your DomainObject
class is not immutable and you change the state of the object while it is in the map in a way that would change the result of calling hashCode
or equals
.
请注意,如果您的DomainObject
类不是不可变的,并且您更改对象在映射中时的状态会改变调用hashCode
或的结果,那么您将遇到麻烦equals
。
hashCode
must be implemented in such a way that it returns the same value for two objects whenever equals
returns true when comparing these objects. See the API documentation of java.lang.Object.hashCode()
for detailed information.
hashCode
必须以这样的方式实现,即equals
在比较这些对象时,只要返回 true ,它就会为两个对象返回相同的值。有关java.lang.Object.hashCode()
详细信息,请参阅 API 文档。
回答by Nicktar
map.get(do)
returning null
could be easily explained by assuming that the Boolean
value for that key might be null
but map.containsKey(do)
returning false
would require do
's hashCode
to be different at the time of calling containsKey(do)
to it's hashCode
at the time of retrieving it from the keySet
.
map.get(do)
返回null
可以通过假设该Boolean
键的值可能是,null
但map.containsKey(do)
返回false
将要求do
'hashCode
在调用containsKey(do)
它hashCode
时与从keySet
.
To see what's happening, you could (temporarily) use a more verbose implementation of HashMap... Maybe something like this:
要查看发生了什么,您可以(暂时)使用更详细的 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);
}
}
You'd need to map all the other requirements of the Map interface to your internalMap as well.
您还需要将 Map 接口的所有其他要求映射到您的 internalMap。
Note: This code is not intended for production, nor is it in any way performance oriented, nice or unsmelly....
注意:此代码不是用于生产,也不是以任何方式面向性能,好或难闻....
2nd note (after seeing your code): To use your domain-object as a key for your hashMap, you should only use the immutable parts of your object for hashCode and equals (in this case the id-value). Else lazy-loading further values would change the hashCode...
第二个注意事项(在看到您的代码后):要将域对象用作 hashMap 的键,您应该只将对象的不可变部分用于 hashCode 和 equals(在本例中为 id 值)。否则延迟加载更多的值会改变 hashCode ......
In Response to your comment:
回应您的评论:
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;
}
}
}
prints
印刷
true
false
The HashCode for the key is computed at the time of putting it into the map.
键的 HashCode 是在将其放入映射时计算的。