对于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 }
View Code

一个重要的内部接口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 }
View Code

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     
View Code

运行结果是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);

  }

  ...

}

 

posted on 2013-11-24 17:14  happinessqi  阅读(493)  评论(0)    收藏  举报