[英]Java HashSet vs HashMap
我了解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;
}
}
該地圖擁有唯一鍵。 當您使用映射中存在的鍵調用put
,該鍵下的對象將被新對象替換。 因此大小為1。
兩者之間的區別應該很明顯:
Map
您存儲鍵值對 Set
您僅存儲密鑰 實際上, HashSet
具有HashMap
字段,每當調用add(obj)
時,都會在基礎地圖map.put(obj, DUMMY)
上調用put
方法-其中,虛擬對象是private static final Object DUMMY = new Object()
。 因此,在地圖中以您的對象作為鍵填充了該值,而這個值沒有意義。
Map
的鍵只能映射到單個值。 所以你第二次put
到地圖使用相同的密鑰,它覆蓋的第一項。
對於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());
我只是想在這些絕妙的答案中添加最后一個難題的答案。 您想知道這兩個集合之間的區別是什么,如果它們在插入后返回相同的大小。 好吧,您實際上看不到其中的區別,因為您要在地圖中使用相同的鍵插入兩個值,從而用第二個值更改第一個值。 如果您在地圖中插入了相同的值 ,但是使用了不同的key ,您將看到真正的區別(以及其他)。 然后,您會看到映射中可以有重復的值 ,但是不能有重復的鍵 ,而在集合中則不能有重復的值 。 這是這里的主要區別。
答案很簡單,因為它是HashSets的本質。 HashSet在內部使用HashMap,並將偽對象PRESENT作為值,並且此哈希圖的KEY將成為您的對象。
hash(simpleObject1)和hash(simplObject2)將返回相同的int。 所以?
當您將simpleObject1添加到哈希集時,它將以simpleObject1作為鍵將其放入其內部哈希圖中。 然后,當您添加(simplObject2)時,您將得到false,因為它已在內部哈希圖中用作鍵。
作為一點額外的信息,HashSet通過使用對象的equals()和hashCode()協定有效地使用了哈希函數來提供O(1)性能。 這就是為什么hashset不允許“ null”的原因,該null無法實現對非對象的equals()和hashCode()。
我認為主要區別在於,HashSet在某種意義上是穩定的,它不會替換重復值(如果在插入第一個唯一鍵之后發現,則將所有將來的重復值丟棄),並且HashMap將努力用新的重復值替換舊值。 因此,HashMap中必須有插入新重復項的開銷。
public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, Serializable
此類實現Set接口,該接口由哈希表(實際上是HashMap實例)支持。 它不保證集合的迭代順序。 特別是,它不能保證順序會隨着時間的推移保持恆定。 此類允許使用null元素。
該類為基本操作(添加,刪除,包含和大小)提供恆定的時間性能,假設哈希函數將元素正確地分散在存儲桶中。 遍歷此集合需要的時間與HashSet實例的大小(元素的數量)加上后備HashMap實例的“容量”(存儲桶的數量)之和成比例。 因此,如果迭代性能很重要,則不要將初始容量設置得過高(或負載因子過低),這一點非常重要。
請注意,此實現未同步。 如果多個線程同時訪問哈希集,並且至少有一個線程修改了哈希集,則必須在外部對其進行同步。 這通常是通過對自然封裝了該集合的某個對象進行同步來實現的。 如果不存在這樣的對象,則應使用Collections.synchronizedSet方法將其“包裝”。 最好在創建時完成此操作,以防止意外同步訪問集。 更多詳細信息
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.