对于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(Entrye = 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 Entrye = table[bucketIndex];4 table[bucketIndex] = new Entry (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 HashSetTest23 {24 public static void main(String[] args)25 {26 Sets = new HashSet ();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);
}
...
}