集合-Java HashSet与HashMap
我了解HashSet是基于HashMap实现的,但是在需要唯一的元素集时使用。 那么,为什么在下一个代码中将相同的对象放入地图并进行设置时,两个集合的大小都等于1? 地图大小不应该为2吗? 因为如果两个集合的大小相等,那么使用这两个集合不会有任何区别。
Set testSet = new HashSet();
Map testMap = new HashMap();
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;
}
}
IgorDiy asked 2019-12-25T07:03:48Z
7个解决方案
129 votes
该地图拥有唯一键。 当您使用映射中存在的键调用HashSet时,该键下的对象将被新对象替换。 因此大小为1。
两者之间的区别应该很明显:
在HashSet中存储键值对
在HashSet中,您仅存储密钥
实际上,HashSet具有HashMap字段,并且每当调用add(obj)时,就会在基础映射map.put(obj, DUMMY)上调用put方法-虚拟对象是private static final Object DUMMY = new Object()。因此,该映射以您的对象作为键并填充了一个值 没有兴趣。
Bozho answered 2019-12-25T07:04:25Z
7 votes
Map中的键只能映射到单个值。 因此,第二次使用相同的密钥将put插入地图时,它将覆盖第一个条目。
ColinD answered 2019-12-25T07:04:46Z
6 votes
对于HashSet,添加相同的对象或多或少是无操作的。 如果是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());
lobster1234 answered 2019-12-25T07:05:06Z
1 votes
我只是想在这些绝妙的答案中添加最后一个难题的答案。 您想知道这两个集合之间的区别是什么,如果它们在插入后返回相同的大小。 好吧,您在这里看不到真正的区别,因为您要使用相同的键在地图中插入两个值,从而用第二个值更改第一个值。 如果您在地图中插入了相同的值,但使用了不同的键,则将看到真正的区别(以及其他)。 然后,您会看到映射中可以有重复的值,但是不能有重复的键,在集合中也不能有重复的值。 这是这里的主要区别。
Nemanja Paripovic answered 2019-12-25T07:05:28Z
1 votes
答案很简单,因为它是HashSets的本质。HashSet在内部使用HashMap,并将虚拟对象命名为PRESENT作为值,此哈希图的KEY将成为您的对象。
hash(simpleObject1)和hash(simplObject2)将返回相同的int。 所以?
当您将simpleObject1添加到哈希集时,它将以simpleObject1作为键将其放入其内部哈希图中。 然后,当您添加(simplObject2)时,您将得到false,因为它已经在内部哈希图中作为键可用。
作为一点额外的信息,HashSet通过使用对象的equals()和hashCode()协定有效地使用了哈希函数来提供O(1)性能。 这就是为什么hashset不允许将“ null”(无法实现的equals()和hashCode())实现为非对象的原因。
huseyin answered 2019-12-25T07:06:02Z
0 votes
我认为主要的区别是HashSet在某种意义上是稳定的,它不会替换重复值(如果在插入第一个唯一键之后发现,则将丢弃所有将来的重复值),并且HashMap将努力用新的重复值替换旧的值。 因此,HashMap中必须有插入新重复项的开销。
David Prun answered 2019-12-25T07:06:23Z
0 votes
public class HashSet
extends AbstractSet
implements Set, Cloneable, Serializable
此类实现Set接口,由哈希表(实际上是HashMap实例)支持。 它不保证集合的迭代顺序。 特别是,它不能保证顺序会随着时间的推移保持恒定。 此类允许使用null元素。
该类为基本操作(添加,删除,包含和大小)提供恒定的时间性能,假设哈希函数将元素正确地分散在存储桶中。 对此集合进行迭代需要的时间与HashSet实例的大小(元素的数量)加上后备HashMap实例的“容量”(存储桶的数量)之和成比例。 因此,如果迭代性能很重要,则不要将初始容量设置得过高(或负载因子过低),这一点非常重要。
请注意,此实现未同步。 如果多个线程同时访问哈希集,并且至少有一个线程修改了哈希集,则必须在外部对其进行同步。 通常,通过在自然封装了该集合的某个对象上进行同步来实现。 如果不存在这样的对象,则应使用Collections.synchronizedSet方法将其“包装”。 最好在创建时完成此操作,以防止意外同步访问集更多细节
Jitendra answered 2019-12-25T07:06:58Z