如何在Java映射中将集合用作键
我有一个映射,它使用一个集合作为键类型,如下所示:如何在Java映射中将集合用作键,java,collections,Java,Collections,我有一个映射,它使用一个集合作为键类型,如下所示: Map<Set<Thing>, Val> map; Map; 当我查询map.containsKey(mybunchhofthings)时,它返回false,我不明白为什么。我可以遍历键集中的每个键,并验证是否有一个键(1)具有相同的hashCode,以及(2)是否等于myBunchOfThings() System.out.println(map.containsKey(myBunchOfThings)); //
Map<Set<Thing>, Val> map;
Map;
当我查询map.containsKey(mybunchhofthings)时,它返回false,我不明白为什么。我可以遍历键集中的每个键,并验证是否有一个键(1)具有相同的hashCode,以及(2)是否等于myBunchOfThings()
System.out.println(map.containsKey(myBunchOfThings)); // false.
for (Set<Thing> k : map.keySet()) {
if (k.hashCode() == myBunchOfThings.hashCode() && k.equals(myBunchOfThings) {
System.out.println("Fail at life."); // it prints this.
}
}
System.out.println(map.containsKey(myBunchOfThings));//错。
for(Set k:map.keySet()){
if(k.hashCode()==myBunchOfThings.hashCode()&&k.equals(myBunchOfThings){
System.out.println(“在生命中失败”);//它打印这个。
}
}
我只是从根本上误解了Containeskey的合同吗?使用集合(或者更一般地说,集合)有秘密吗作为映射的键?插入后是否修改了该集?如果是,则该集可能被排序到与其正在查找的存储桶不同的存储桶中。迭代时,它确实会找到您的集,因为它在整个映射中查找
我相信HashMap的合同规定不允许修改用作键的对象的hashcode,是否传递了确切的集合(您要查找的集合)在比较键时?您应该努力使用不可变类型作为
Map
s的键。集合和集合通常非常容易变化,因此使用这种方式通常不是一个好主意
如果您想使用许多键值作为Map
键,那么应该使用为此目的而设计的类实现,比如apachecommons集合
如果确实必须将集合或集合用作键,请首先将其设置为不可变(Collections.unmodifiableSet(…)
),然后不要保留对可变备份对象的引用
将集合用作键的另一个困难是,它们可以以不同的顺序构造。只有已排序的集合才有很高的匹配可能性。例如,如果使用顺序排列的ArrayList
,但第二次以不同的方式构造列表,则该列表将与键不匹配-哈希代码d值的顺序不同
编辑:我在下面的陈述中得到了纠正,我从未对ket使用过Set。我只是在AbstractHashSet中读取了hashCode实现的一部分。这使用了所有值的简单合计,因此不依赖于顺序。Equals还检查一个集合是否包含另一个集合中的所有值。但是,这仍然是true与Java中其他类型的集合(ArrayList顺序并不重要)
如果您的集合实际上是一个
哈希集
,那么创建顺序也会很重要。事实上,任何类型的哈希管理集合都会有更大的问题,因为任何容量更改都会触发整个集合的重建,从而可以对元素进行重新排序。请考虑按照冲突顺序存储的哈希冲突ur(所有元素的简单链接链,其中转换的散列值相同)。键在映射中使用时不应发生变异。映射
java文档说明:
注意:如果发生以下情况,必须非常小心
可变对象用作贴图关键点。
未指定映射的行为
如果对象的值已更改
以影响平等的方式
对象是键时的比较
在地图上。这是一个特例
禁止是因为它不是
允许地图包含
它本身就是一把钥匙
允许地图包含
作为一种价值观,极端谨慎是必要的
建议:equals和hashCode
方法不再在上定义良好
一张这样的地图
我知道这个问题,但直到现在才做过测试。我再详细说明一下:
Map<Set<String>, Object> map = new HashMap<Set<String>, Object>();
Set<String> key1 = new HashSet<String>();
key1.add( "hello");
Set<String> key2 = new HashSet<String>();
key2.add( "hello2");
Set<String> key2clone = new HashSet<String>();
key2clone.add( "hello2");
map.put( key1, new Object() );
map.put( key2, new Object() );
System.out.println( map.containsKey(key1)); // true
System.out.println( map.containsKey(key2)); // true
System.out.println( map.containsKey(key2clone)); // true
key2.add( "mutate" );
System.out.println( map.containsKey(key1)); // true
System.out.println( map.containsKey(key2)); // false
System.out.println( map.containsKey(key2clone)); // false (*)
key2.remove( "mutate" );
System.out.println( map.containsKey(key1)); // true
System.out.println( map.containsKey(key2)); // true
System.out.println( map.containsKey(key2clone)); // true
Map Map=newhashmap();
Set key1=新的HashSet();
键1.添加(“你好”);
Set key2=新的HashSet();
键2.添加(“hello2”);
Set key2clone=new HashSet();
键2克隆。添加(“hello2”);
put(key1,newobject());
put(键2,newobject());
System.out.println(map.containsKey(key1));//true
System.out.println(map.containsKey(key2));//true
System.out.println(map.containsKey(key2clone));//true
键2.添加(“变异”);
System.out.println(map.containsKey(key1));//true
System.out.println(map.containsKey(key2));//false
System.out.println(map.containsKey(key2clone));//false(*)
键2.删除(“变异”);
System.out.println(map.containsKey(key1));//true
System.out.println(map.containsKey(key2));//true
System.out.println(map.containsKey(key2clone));//true
key2
变异后,地图不再包含它。我们可以认为地图在添加数据时“索引”了数据,我们希望它仍然包含key2克隆(标有*
的行)。但有趣的是,事实并非如此
因此,正如java文档所说,键不应该变异,否则行为是未指定的
我猜你的情况就是这样。可能不是,但这并不重要。根据java文档,如果两个集合具有相同的元素,则无论其顺序如何,都是相等的。即使对于
SortedSet
SortedSet
也可以使用迭代器更改集合的遍历,但不能更改相等。应该考虑元素的顺序仅在列表中列出。您是否肯定您对HashSet
的评论?“任何容量更改都会触发整个集合的重建,从而可以对元素进行重新排序”这是正确的,但我认为ewernli的equals()和hashCode()是正确的方法的编写是为了避免因为构造不同而导致两个相等的集合被比较为不相等。我将避开Apache集合的内容,因为它看起来没有被积极维护。至少,它看起来不支持泛型,这使得我的“远离”神经元起火。@Gabe:a贪婪和我在谷歌收藏中没有找到一个好的选择