HashSet源码分析1

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class SetTest {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();
        set.add("abc");
        set.add("xyz");
        set.add("abc");

        for (Iterator<String> it = set.iterator(); it.hasNext();) {
            System.out.println(it.next());
        }
    }
}

查看add方法

/**
     * Adds the specified element to this set if it is not already present.
     * More formally, adds the specified element <tt>e</tt> to this set if
     * this set contains no element <tt>e2</tt> such that
     * <tt>(e==null&nbsp;?&nbsp;e2==null&nbsp;:&nbsp;e.equals(e2))</tt>.
     * If this set already contains the element, the call leaves the set
     * unchanged and returns <tt>false</tt>.
     *
     * @param e element to be added to this set
     * @return <tt>true</tt> if this set did not already contain the specified
     * element
     */
    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

查看map.put方法:

 /**
     * Associates the specified value with the specified key in this map.
     * If the map previously contained a mapping for the key, the old
     * value is replaced.
     *
     * @param key key with which the specified value is to be associated
     * @param value value to be associated with the specified key
     * @return the previous value associated with <tt>key</tt>, or
     *         <tt>null</tt> if there was no mapping for <tt>key</tt>.
     *         (A <tt>null</tt> return can also indicate that the map
     *         previously associated <tt>null</tt> with <tt>key</tt>.)
     */
    public V put(K key, V value) {
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key);
        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);
        return null;
    }

关于set:当向Set集合中添加对象时,集合首先要计算出待添加对象的hashCode值,根据该值来得到一个位置,用于存放当前的对象。如果该位置没有对象存在,那么集合Set就会认为该对象在集合中不存在,直接添加进去。如果该位置已经有一个对象存在,接着将准备添加到集合中的对象与该位置上的对象进行equals方法比较,如果该equals方法返回false,那么集合认为该集合中不存在该对象的,再进行一次散列,将该对象放到散列后计算的地址里。如果equals方法返回true,那么集合认为该对象已经存在了,不会再将对象添加到集合中。

在上面的例子中我们的执行结果为:

true
true
false
abc
xyz

第三个abc插入失败了,原因就是String类型的字符串“abc”的hashcode是相等的而且equals也是相等的(equals方法使一个字符一个字符的进行比较)。


再看下面的例子:

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class SetTest {
    public static void main(String[] args) {
    /*    Set<String> set = new HashSet<>();
        System.out.println(set.add("abc"));
        System.out.println(set.add("xyz"));
        System.out.println(set.add("abc"));

        for (Iterator<String> it = set.iterator(); it.hasNext();) {
            System.out.println(it.next());
        }*/
        Set<People> set2 = new HashSet<>();
        set2.add(new People("zhangsan"));
        set2.add(new People("lisi"));
        set2.add(new People("zhangsan"));
        for(Iterator<People> it = set2.iterator();it.hasNext();){
            System.out.println(it.next().getName());
        }
    }
}
class People{
    String name;
    public People(String name){
        this.name = name;
    }
    public String getName(){
        return this.name;
    }
}

运行结果为:

lisi
zhangsan
zhangsan

同样是zhangsan却能添加两次。

如果想根据name来判断,修改代码如下:

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class SetTest {
    public static void main(String[] args) {
        /*
         * Set<String> set = new HashSet<>();
         * System.out.println(set.add("abc"));
         * System.out.println(set.add("xyz"));
         * System.out.println(set.add("abc"));
         * 
         * for (Iterator<String> it = set.iterator(); it.hasNext();) {
         * System.out.println(it.next()); }
         */
        /*
         * String a = "abc"; String b = "abc"; System.out.println(a.hashCode());
         * System.out.println(b.hashCode());
         */
        Set<People> set2 = new HashSet<>();
        set2.add(new People("zhangsan"));
        set2.add(new People("lisi"));
        set2.add(new People("zhangsan"));
        for (Iterator<People> it = set2.iterator(); it.hasNext();) {
            System.out.println(it.next().getName());
        }
    }
}

class People {
    String name;

    public People(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj instanceof People) {
            People people = (People) obj;
            if(this.name.equals(people.getName()));
                return true;
        }
        return false;
    }
    @Override
    public int hashCode() {
        return this.name.hashCode();
    }
}

执行结果为:

lisi
zhangsan

当重写equals方法时,必须要重写hashCode方法。如果一个类的两个对象,使用equals方法比较时返回true,那么这两个对象必须要具有相同的hashCode。

原文地址:https://www.cnblogs.com/vincent4code/p/4798175.html