集合HashSet的使用


集合中的HashSet底層是通過Hash表實現,HashSet的特點是元素唯一,但用到Hash表就跟hashCode()有了密不可分的聯系,所以HashSet的唯一性是通過hashCode()方法來保證,當然光有HashCode()還不夠,還有equals()也用到。從底層(HashMap的put()方法)實現代碼來看,就可以清楚地看到這一點。

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

以上代碼是HashMap的put()方法源碼。那么HashSet到底是如何保證元素的唯一性,還是通過例子來說明這一點。同樣,還是以HashSet存儲自定義對象為例,先創建一個Person類,成員:name、age、無參、有參構造及getters和setters。

package cn.dolphin;

public class Person{
    private String name;
    private int age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public Person(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }
    public Person() {
        super();
        // TODO Auto-generated constructor stub
    }
    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }    
}

這里先沒有重寫繼承自Object類的hashCode()和equals()方法。

package cn.dolphin;

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

public class HashSetDemo {
    public static void main(String[] args) {
        //創建集合對象
        Set<Person> set = new HashSet<>();
        //創建對象元素
        Person p1 = new Person("諸葛亮",39);
        Person p2 = new Person("趙子龍",36);
        Person p3 = new Person("關雲長",38);
        Person p4 = new Person("關雲長",38);
        Person p5 = new Person("關雲長",38);
        //將對象元素添加到集合
        set.add(p1);
        set.add(p2);
        set.add(p3);
        set.add(p4);
        set.add(p5);
        //使用iterator()對集合遍歷
        for (Iterator<Person> it = set.iterator(); it.hasNext();) {
            Person p = it.next();
            System.out.println(p);
        }
    }
}

運行程序的結果看到[關雲長,38]出現了三次,這說明沒有保證元素的唯一。現在我們在Person類中重寫hashCode()和equals()。使用eclipse直接右鍵"Source->Generate hashCode() and equals()..."自動生成代碼。

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + age;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (!(obj instanceof Person))
            return false;
        Person other = (Person) obj;
        if (age != other.age)
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }

再次運行程序,看到結果中[關雲長,38]只出現了一次,說明已經對相同元素進行了過濾。再回過頭來看看上面的源代碼,里面的if語句"if (e.hash == hash && ((k = e.key) == key || key.equals(k)))"使用的"&&",這個邏輯運算符的使用,還是有必要在這里啰嗦一下,只有"&&"的左邊為true才會對右邊進行判斷,左邊如果false,就不再看右邊,這不禁使我想起一道面試題,扯遠了,這個最后說。那么"&&"用在這里就意味着,如果左邊的hashCode()判斷false,會直接添加元素,不用再判斷equals(),如果左邊true,才會繼續判斷equals()。因為"&&"具有短路功能,這就是HashSet保證元素唯一的原理。
  下面說說剛才提到的面試題。

        int x = 1, y = 1;
        if(x++ > 3 && ++y > 3){
            ++x;
            y++;
        }
        System.out.println(x);
        System.out.println(y);        


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM