Java集合实际是多个引用变量组成的集合,修改引用对象的属性后表现在集合内对象的属性也会相应被修改。但是可验证,如果集合内元素的类型是包装类类型,如Integer,则不满足上述称述。
对Map而言,每个元素都是key-value的Set集合。HashSet的实现基于HashMap,HashMap的实现用到了Entry数组。于是,可以将Set集合扩展成Map集合。
本文主要阅读HashSet和HashMap的JDK源码,了解其实现。
HashSet源码如下:
public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable { static final long serialVersionUID = -5024744406713321676L; private transient HashMap<E,Object> map; //基于HashMap // Dummy value to associate with an Object in the backing Map private static final Object PRESENT = new Object(); //private static final Object /** * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has * default initial capacity (16) and load factor (0.75). */ public HashSet() { map = new HashMap<E,Object>(); }//后面代码略........
可以看到HashSet基于HashMap来实现的,下面来看HashMap的实现。
下面通过HashMap的两个重要的方法get 和put 方法来认识HashMap。
public V get(Object key) { //HashMap的方法 if (key == null) return getForNullKey(); int hash = hash(key.hashCode()); for (Entry<K,V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) //此处可见hashCode提升性能!!!!! return e.value; } return null; }
public V put(K key, V value) { //HashMap的方法 if (key == null) return putForNullKey(value); int hash = hash(key.hashCode()); int i = indexFor(hash, table.length); for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { //示例 V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; addEntry(hash, key, value, i);//优雅地将新Entry加至链头。Entry是HashMap的内部类。具体请阅读jdk源码。 return null; }
例子1:
import java.util.HashSet; import java.util.Set; class Name { private String first; private String last; public Name(String first, String last) { this.first = first; this.last = last; } public boolean equals(Object o) { if(!(o instanceof Name)) return false; Name n = (Name)o; return n.first.equals(first) && n.last.equals(last); } } public class HashSetTest { public static void main(String[] args) { Set<Name> s = new HashSet<Name>(); s.add(new Name("zhang","3")); System.out.println(s.contains(new Name("zhang", "3"))); //false } }程序将输出false是因为HashSet判断是否“相等”的标准除了要求通过equals方法返回true外,还要求两个对象的HashCode()返回值相等,且这两个方法的返回值保持一致。通常来说,所有参与hashCode()计算的关键属性都应作为equals方法比较的标准(参见上一篇Java中hashcode和equals方法)。
例子2
import java.util.HashSet; import java.util.Set; class Name { private String first; private String last; public Name(String first, String last) { this.first = first; this.last = last; } public boolean equals(Object o) { if(!(o instanceof Name)) return false; Name n = (Name)o; return n.first.equals(first); } @Override public int hashCode() { return first.hashCode(); } @Override public String toString() { return "Name[" + first + ", " + last + "]"; } } public class HashSetTest2 { public static void main(String[] args) { Set<Name> s = new HashSet<Name>(); s.add(new Name("zhang","3")); s.add(new Name("zhang","4")); //无法覆盖 System.out.println(s); //输出 [Name[zhang, 3]] } }