Java API —— Set接口 & HashSet類 & LinkedHashSet類

一、Set接口
    1)Set接口概述
        一個不包含重複元素的 collection,無序(存儲順序和取出順序不一致),惟一。  (List有序,即存儲順序和取出順序一致,可重複)
    2)Set案例
        存儲字符串並遍歷
        存儲自定義對象並遍歷
 
二、HashSet
    1)HashSet類概述
        不保證 set 的迭代順序;特別是它不保證該順序恆久不變。
    2)HashSet如何保證元素惟一性
        底層數據結構是哈希表(元素是鏈表的數組)
        哈希表依賴於哈希值存儲
        添加功能底層依賴兩個方法:
            · int hashCode()
            · boolean equals(Object obj)
例子1:存儲字符串
package setdemos;
import java.util.HashSet;
import java.util.Set;
/**
 * Created by gao on 15-12-17.
 */
public class HashSetDemo01 {
    public static void main(String[] args) {
        //建立集合對象
        Set<String> set = new HashSet<String>();
        //建立並添加元素
        set.add("hello");
        set.add("java");
        set.add("world");
        set.add("java");
        set.add("android");
        set.add("hello");
        //加強for
        for(String s : set){
            System.out.println(s);
        }
    }
}
輸出結果:(不存儲重複的)
hello
android
java
world
 
例子2:存儲自定義對象
學生類:重寫hashCode()方法和equals()方法
package setdemos;
/**
 * @author Administrator
 * 
 */
public class Student {
    private String name;
    private int age;
    public Student() {
        super();
    }
    public Student(String name, int age) {
        super();
        this.name = name;
        this.age = 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;
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Student)) return false;
        Student student = (Student) o;
        if (age != student.age) return false;
        if (!name.equals(student.name)) return false;
        return true;
    }
    @Override
    public int hashCode() {
        int result = name.hashCode();
        result = 31 * result + age;
        return result;
    }
}

測試類:java

package setdemos;
import java.util.HashSet;
/**
 * Created by gao on 15-12-17.
 */
/*
 * 需求:存儲自定義對象,並保證元素的惟一性
 * 要求:若是兩個對象的成員變量值都相同,則爲同一個元素。
 *
 * 目前是不符合個人要求的:由於咱們知道HashSet底層依賴的是hashCode()和equals()方法。
 * 而這兩個方法咱們在學生類中沒有重寫,因此,默認使用的是Object類。
 * 這個時候,他們的哈希值是不會同樣的,根本就不會繼續判斷,執行了添加操做。
 */
public class HashSetDemo02 {
    public static void main(String[] args) {
        // 建立集合對象
        HashSet<Student> hs = new HashSet<Student>();
        // 建立學生對象
        Student s1 = new Student("林青霞", 27);
        Student s2 = new Student("柳巖", 22);
        Student s3 = new Student("王祖賢", 30);
        Student s4 = new Student("林青霞", 27);
        Student s5 = new Student("林青霞", 20);
        Student s6 = new Student("范冰冰", 22);
        // 添加元素
        hs.add(s1);
        hs.add(s2);
        hs.add(s3);
        hs.add(s4);
        hs.add(s5);
        hs.add(s6);
        // 遍歷集合
        for (Student s : hs) {
            System.out.println(s.getName() + "---" + s.getAge());
        }
    }
}
輸出結果:
王祖賢---30
范冰冰---22
林青霞---27
林青霞---20
柳巖---22
 
     3) HashSet集合的add()方法的源碼
     問題:爲何存儲字符串的時候,字符串內容相同的只存儲了一個呢?
       經過查看add方法的源碼,咱們知道這個方法底層依賴 兩個方法:hashCode()和equals()。
     步驟:
    首先比較哈希值
    若是相同,繼續走,比較地址值或者走equals()
    若是不一樣,就直接添加到集合中
     按照方法的步驟來講:
    先看hashCode()值是否相同
    相同:繼續走equals()方法
    返回true: 說明元素重複,就不添加
    返回false:說明元素不重複,就添加到集合
    不一樣:就直接把元素添加到集合
       若是類沒有重寫這兩個方法,默認使用的Object()。通常來講不會相同。
       而String類重寫了hashCode()和equals()方法,因此,它就能夠把內容相同的字符串去掉。只留下一個。
interface Collection {
    ...
}
interface Set extends Collection {
    ...
}
class HashSet implements Set {
    private static final Object PRESENT = new Object();
    private transient HashMap<E,Object> map;
    
    public HashSet() {
        map = new HashMap<>();
    }
    
    public boolean add(E e) { //e=hello,world
        return map.put(e, PRESENT)==null;
    }
}
class HashMap implements Map {
    public V put(K key, V value) { //key=e=hello,world
    
        //看哈希表是否爲空,若是空,就開闢空間
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
        
        //判斷對象是否爲null
        if (key == null)
            return putForNullKey(value);
        
        int hash = hash(key); //和對象的hashCode()方法相關
        
        //在哈希表中查找hash值
        int i = indexFor(hash, table.length);
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            //此次的e實際上是第一次的world
            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;
    }
    
    transient int hashSeed = 0;
    
    final int hash(Object k) { //k=key=e=hello,
        int h = hashSeed;
        if (0 != h && k instanceof String) {
            return sun.misc.Hashing.stringHash32((String) k);
        }
        h ^= k.hashCode(); //這裏調用的是對象的hashCode()方法
        // This function ensures that hashCodes that differ only by
        // constant multiples at each bit position have a bounded
        // number of collisions (approximately 8 at default load factor).
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }
}
hs.add("hello");
hs.add("world");
hs.add("java");
hs.add("world");

 

     4)HashSet存儲元素保證惟一性的代碼及圖解android

 

 

  5)Set集合練習:
        HashSet集合存儲自定義對象並遍歷。若是對象的成員變量值相同即爲同一個對象
自定義類Dog類:
package hashsetdemos;
/**
 * Created by gao on 15-12-17.
 */
public class Dog {
    private String name;
    private int age;
    private String color;
    private char sex;
    public Dog() {
    }
    public Dog(String name, int age, String color, char sex) {
        this.name = name;
        this.age = age;
        this.color = color;
        this.sex = sex;
    }
    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 String getColor() {
        return color;
    }
    public void setColor(String color) {
        this.color = color;
    }
    public char getSex() {
        return sex;
    }
    public void setSex(char sex) {
        this.sex = sex;
    }
    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", color='" + color + '\'' +
                ", sex=" + sex +
                '}';
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Dog)) return false;
        Dog dog = (Dog) o;
        if (age != dog.age) return false;
        if (sex != dog.sex) return false;
        if (!color.equals(dog.color)) return false;
        if (!name.equals(dog.name)) return false;
        return true;
    }
    @Override
    public int hashCode() {
        int result = name.hashCode();
        result = 31 * result + age;
        result = 31 * result + color.hashCode();
        result = 31 * result + (int) sex;
        return result;
    }
}

測試類:數組

package hashsetdemos;
import java.util.HashSet;
/**
 * Created by gao on 15-12-17.
 */
/*
 * HashSet集合存儲自定義對象並遍歷。若是對象的成員變量值相同即爲同一個對象
 *
 * 注意了:
 *         你使用的是HashSet集合,這個集合的底層是哈希表結構。
 *         而哈希表結構底層依賴:hashCode()和equals()方法。
 *         若是你認爲對象的成員變量值相同即爲同一個對象的話,你就應該重寫這兩個方法。
 *         如何重寫呢?不一樣擔憂,自動生成便可。
 */
public class Exercise01 {
    public static void main(String[] args) {
        // 建立集合對象
        HashSet<Dog> hs = new HashSet<Dog>();
        // 建立狗對象
        Dog d1 = new Dog("秦檜", 25, "紅色", '男');
        Dog d2 = new Dog("高俅", 22, "黑色", '女');
        Dog d3 = new Dog("秦檜", 25, "紅色", '男');
        Dog d4 = new Dog("秦檜", 20, "紅色", '女');
        Dog d5 = new Dog("魏忠賢", 28, "白色", '男');
        Dog d6 = new Dog("李蓮英", 23, "黃色", '女');
        Dog d7 = new Dog("李蓮英", 23, "黃色", '女');
        Dog d8 = new Dog("李蓮英", 23, "黃色", '男');
        //添加元素
        hs.add(d1);
        hs.add(d2);
        hs.add(d3);
        hs.add(d4);
        hs.add(d5);
        hs.add(d6);
        hs.add(d7);
        hs.add(d8);
        //遍歷
        for (Dog d : hs) {
            System.out.println(d.getName() + "---" + d.getAge() + "---" + d.getColor() + "---" + d.getSex());
        }
    }
}
輸出結果:
李蓮英---23---黃色---男
魏忠賢---28---白色---男
李蓮英---23---黃色---女
秦檜---25---紅色---男
秦檜---20---紅色---女
高俅---22---黑色---女
 
三、LinkedHashSet類
    具備可預知迭代順序的 Set 接口的哈希表和連接列表實現。此實現與 HashSet 的不一樣以外在於,後者維護着一個運行於全部條目的雙重連接列表。此連接列表定義了迭代順序,即按照將元素插入到 set 中的順序(插入順序)進行迭代。注意,插入順序不 受在 set 中從新插入的 元素的影響。
    LinkedHashSet:底層數據結構由哈希表和鏈表組成。
    哈希表保證元素的惟一性。
    鏈表保證元素有素。(存儲和取出是一致)
package linkedhashset;
import java.util.LinkedHashSet;
/**
 * Created by gao on 15-12-17.
 */
public class LinkedHashSetDemo {
    public static void main(String[] args) {
        // 建立集合對象
        LinkedHashSet<String> hs = new LinkedHashSet<String>();
        
        // 建立並添加元素
        hs.add("hello");
        hs.add("world");
        hs.add("java");
        hs.add("world");
        hs.add("java");
        // 遍歷
        for (String s : hs) {
            System.out.println(s);
        }
    }
}
相關文章
相關標籤/搜索