对于HashSet而言,系统采用Hash算法决定集合元素的存储位置,这样可以保证快速存取集合元素;
对于HashMap,系统将value当成key的附属,系统根据Hash算法来决定key的存储位置,这样可以保证快速存取集合key,而value总是紧随key存储。
(这些集合虽然号称存储的是java对象,但实际上并不会真正将java对象放入set集合中,而只是在Set集合中保留这些对象的引用。
当程序视图将多个key-value 放入HashMap中时,采用一种“Hash算法”来决定每个元素的存储位置。
1 public V put(K key, V value) 2 { 3 if(key == null) 4 return putForNullKey(value); 5 int hash = hash(key.hashCode()); 6 7 int i = indexFor(hash, table.length); 8 9 for(Entry<K,V> e = table[i]; e!=null;e=e.next) 10 { 11 Object K; 12 if(e.hash==hash && ((k=e.key) == k || key.equals(k)) 13 { 14 V oldValue = e.value; 15 e.value= value; 16 e.recordAccess(this); 17 return oldValue; 18 } 19 } 20 modCount ++; 21 addEnrty(hash,key,value,i); 22 return null; 23 }
一个重要的内部接口Map.Entry,每个Map.Entry其实就是一个Key-Value对。当系统决定存储HashMap中的key-value对是,只是根据key来计算并决定每个Entry的存储位置:如果两个Entry的key的hashCode()返回值相同,那么它们的存储位置相同;如果这两个key通过equals比较返回true,新添加的Entry的value将覆盖原有Entry的value,但key不会覆盖;如果这两个key通过equals比较返回false,新添加的Entry将与集合中原有Entry形成Entry链。见addEntry()方法:
1 void addEntry(int hash,K key,V value,int bucketIndex) 2 { 3 Entry<K,V> e = table[bucketIndex]; 4 table[bucketIndex] = new Entry<K,V>(hash,key,value,e); 5 if(size++ >= threshold) //threshold包含HashMap能容纳的key-value对的极限。 6 resize(2*table.length); 7 }
table实质就是一个普通数组,每个数组都有一个固定一的长度,这个数组的长度就是HashMap的容量。
HashSet是基于HashMap实现的,HashSet底层采用HashMap来保存所有元素。
1 class Name 2 { 3 private String first; 4 private String last; 5 public Name(String first, String last) 6 { 7 this.first = first; 8 this.last = last; 9 } 10 public boolean equals(Object o) 11 { 12 if(this == o) 13 return true; 14 if(o.getClass() == Name.class) 15 { 16 Name n =(Name) o; 17 return n.first.equals(first) && n.last.equals(last); 18 } 19 return false; 20 } 21 22 public class HashSetTest 23 { 24 public static void main(String[] args) 25 { 26 Set<Name> s = new HashSet<Name>(); 27 s.add(new Name("abc","123")); 28 System.out.println(s.contains(new Name("abc","123"); 29 } 30 } 31
运行结果是false.
因为HashSet判断两个对象相等的标准除了要求通过equals方法比较返回true外,还要求两个对象的hashCode()返回值相等。
重写hashCode()方法:
public int hashCode()
{
return first.hashCode();
}
public boolean equals(Object o)
{
....
if(o.getClass() == Name.class)
{
Name n = (Name) o;
return n.first.equals(first);
}
...
}