HashSet和HashMap

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]]
                
    }
}

你可能感兴趣的:(HashMap,hashset)