我了解HashSet
基于HashMap
实现,但在需要唯一元素集时使用。那么为什么在下一个代码中将相同的对象放入地图并设置时,我们将两个集合的大小等于1?地图大小不应该是2?因为如果两个集合的大小相等,我认为使用这两个集合没有任何区别。
Set testSet = new HashSet<SimpleObject>();
Map testMap = new HashMap<Integer, SimpleObject>();
SimpleObject simpleObject1 = new SimpleObject("Igor", 1);
SimpleObject simplObject2 = new SimpleObject("Igor", 1);
testSet.add(simpleObject1);
testSet.add(simplObject2);
Integer key = new Integer(10);
testMap.put(key, simpleObject1);
testMap.put(key, simplObject2);
System.out.println(testSet.size());
System.out.println(testMap.size());
输出为1和1.
SimpleObject code
public class SimpleObject {
private String dataField1;
private int dataField2;
public SimpleObject(){}
public SimpleObject(String data1, int data2){
this.dataField1 = data1;
this.dataField2 = data2;
}
public String getDataField1() {
return dataField1;
}
public int getDataField2() {
return dataField2;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((dataField1 == null) ? 0 : dataField1.hashCode());
result = prime * result + dataField2;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
SimpleObject other = (SimpleObject) obj;
if (dataField1 == null) {
if (other.dataField1 != null)
return false;
} else if (!dataField1.equals(other.dataField1))
return false;
if (dataField2 != other.dataField2)
return false;
return true;
}
}
答案 0 :(得分:129)
地图包含唯一键。使用映射中存在的键调用put
时,该键下的对象将替换为新对象。因此大小为1。
两者之间的区别应该是显而易见的:
Map
存储键值对Set
中仅存储密钥事实上,HashSet
有一个HashMap
字段,每当调用add(obj)
时,都会在基础地图put
上调用map.put(obj, DUMMY)
方法 - 虚拟对象是private static final Object DUMMY = new Object()
的位置。因此,地图将填充您的对象作为键,以及一个不感兴趣的值。
答案 1 :(得分:7)
Map
中的密钥只能映射到单个值。因此,当您put
第二次使用相同的密钥进入地图时,它会覆盖第一个条目。
答案 2 :(得分:6)
对于HashSet,添加same对象或多或少都是无操作。对于HashMap,将新键,值对与现有键放在一起将覆盖现有值,以便为该键设置新值。下面我给你的代码添加了equals()检查:
SimpleObject simpleObject1 = new SimpleObject("Igor", 1);
SimpleObject simplObject2 = new SimpleObject("Igor", 1);
//If the below prints true, the 2nd add will not add anything
System.out.println("Are the objects equal? " , (simpleObject1.equals(simpleObject2));
testSet.add(simpleObject1);
testSet.add(simplObject2);
Integer key = new Integer(10);
//This is a no-brainer as you've the exact same key, but lets keep it consistent
//If this returns true, the 2nd put will overwrite the 1st key-value pair.
testMap.put(key, simpleObject1);
testMap.put(key, simplObject2);
System.out.println("Are the keys equal? ", (key.equals(key));
System.out.println(testSet.size());
System.out.println(testMap.size());
答案 3 :(得分:1)
我只想补充这些伟大的答案,解决你最后的两难困境。您想知道这两个集合之间的区别是什么,如果它们在插入后返回相同的大小。好吧,你不能在这里真正看到差异,因为你在地图中用相同的键插入两个值,因此用第二个值改变第一个值。如果您在地图中插入相同值,但是使用不同的键,您会看到真正的差异(在其他之中)。然后,您会在地图中看到可以 重复值,但无法 重复键< / strong>,并且在无法的重复值的集合中。这是主要区别。
答案 4 :(得分:1)
答案很简单,因为它是HashSets的本质。 HashSet使用内部HashMap和名为PRESENT的虚拟对象作为值,此hashmap的 KEY 将是您的对象。
hash(simpleObject1)和hash(simplObject2)将返回相同的int。所以?
当你将simpleObject1添加到hashset时,它将把它放到它的内部hashmap中,并将simpleObject1作为键。然后,当您添加(simplObject2)时,您将得到错误,因为它在内部hashmap中已经可用作键。
作为一个额外的信息,HashSet使用有效的散列函数通过使用object的equals()和hashCode()契约来提供O(1)性能。这就是为什么hashset不允许“null”无法将equals()和hashCode()实现为非对象的原因。
答案 5 :(得分:0)
我认为主要的区别是, HashSet在某种意义上是稳定的,它不会替换重复值(如果在插入第一个唯一键后发现,只丢弃所有未来的重复项),HashMap将努力用新的重复值替换旧的。因此,在插入新的重复项时,HashMap必须有开销。
答案 6 :(得分:0)
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, Serializable
此类实现Set接口,由哈希表(实际上是HashMap实例)支持。它不能保证集合的迭代顺序;特别是,它不保证订单会随着时间的推移保持不变。该类允许null元素。
该类为基本操作(添加,删除,包含和大小)提供恒定的时间性能,假设散列函数在桶之间正确地分散元素。迭代此集合需要的时间与HashSet实例的大小(元素数量)加上&#34;容量&#34;的总和成正比。支持HashMap实例(桶的数量)。因此,如果迭代性能很重要,不要将初始容量设置得太高(或负载因子太低)非常重要。
请注意,此实现未同步。如果多个线程同时访问哈希集,并且至少有一个线程修改了该集,则必须在外部进行同步。这通常通过在自然封装集合的某个对象上进行同步来实现。如果不存在这样的对象,那么该集合应该是&#34; wrap&#34;使用Collections.synchronizedSet方法。这最好在创建时完成,以防止对集合的意外不同步访问 More Details