電腦程式的思維邏輯 (41),思維41

來源:互聯網
上載者:User

電腦程式的思維邏輯 (41),思維41

上節介紹了HashMap,提到了Set介面,Map介面的兩個方法keySet和entrySet返回的都是Set,本節,我們來看Set介面的一個重要實作類別HashSet。

與HashMap類似,字面上看,HashSet由兩個單片語成,Hash和Set,Set表示介面,實現Set介面也有多種方式,各有特點,HashSet實現的方式利用了Hash。

下面,我們先來看HashSet的用法,然後看實現原理,最後我們總結分析下HashSet的特點。

用法

Set介面

Set表示的是沒有重複元素、且不保證順序的容器介面,它擴充了Collection,但沒有定義任何新的方法,不過,對於其中的一些方法,它有自己的規範。

Set介面的完整定義為:

public interface Set<E> extends Collection<E> {    int size();    boolean isEmpty();    boolean contains(Object o);    Iterator<E> iterator();    Object[] toArray();    <T> T[] toArray(T[] a);    boolean add(E e);    boolean remove(Object o);    boolean containsAll(Collection<?> c);    boolean addAll(Collection<? extends E> c);    boolean retainAll(Collection<?> c);    boolean removeAll(Collection<?> c);    void clear();    boolean equals(Object o);    int hashCode();}

與Collection介面中定義的方法是一樣的,不過,一些方法有一些不同的規範要求。

添加元素

boolean add(E e);

如果集合中已經存在相同元素了,則不會改變集合,直接返回false,只有不存在時,才會添加,並返回true。

大量新增

boolean addAll(Collection<? extends E> c);

重複的元素不添加,不重複的添加,如果集合有變化,返回true,沒變化返回false。

迭代器

Iterator<E> iterator();

迭代遍曆時,不要求元素之間有特別的順序。HashSet的實現就是沒有順序,但有的Set實現可能會有特定的順序,比如TreeSet,我們後續章節介紹。

HashSet

與HashMap類似,HashSet的構造方法有:

public HashSet()public HashSet(int initialCapacity)public HashSet(int initialCapacity, float loadFactor)public HashSet(Collection<? extends E> c)

initialCapacity和loadFactor的含義與HashMap中的是一樣的,待會我們再細看。

HashSet的使用也很簡單,比如:

Set<String> set = new HashSet<String>();set.add("hello");set.add("world");set.addAll(Arrays.asList(new String[]{"hello","老馬"}));for(String s : set){    System.out.print(s+" ");}

輸出為:

hello 老馬 world 

"hello"被添加了兩次,但只會儲存一份,輸出也沒有什麼特別的順序。

hashCode與equals

與HashMap類似,HashSet要求元素重寫hashCode和equals方法,且對兩個對象,equals相同,則hashCode也必須相同,如果元素是自訂的類,需要注意這一點。

比如說,有一個表示規格的類Spec,有大小和顏色兩個屬性:

class Spec {    String size;    String color;        public Spec(String size, String color) {        this.size = size;        this.color = color;    }    @Override    public String toString() {        return "[size=" + size + ", color=" + color + "]";    }}

看一個Spec的Set:

Set<Spec> set = new HashSet<Spec>();set.add(new Spec("M","red"));set.add(new Spec("M","red"));System.out.println(set);

輸出為:

[[size=M, color=red], [size=M, color=red]]

同一個規格輸出了兩次,為避免這一點,需要為Spec重寫hashCode和equals方法,利用IDE開發工具往往可以自動產生這兩個方法,比如Eclipse中,可以通過"Source"->"Generate hashCode() and equals() ...",我們就不贅述了。

應用情境

HashSet有很多應用情境,比如說:

  • 排重,如果對排重後的元素沒有順序要求,則HashSet可以方便的用於排重。
  • 儲存特殊值,Set可以用於儲存各種特殊值,程式處理使用者請求或資料記錄時,根據是否為特殊值,進行特殊處理,比如儲存IP地址的黑名單或白名單。
  • 集合運算,使用Set可以方便的進行數學集合中的運算,如交集、並集等運算,這些運算有一些很現實的意義。比如使用者標籤計算,每個使用者都有一些標籤,兩個使用者的標籤交集就表示他們的共同特徵,交集大小除以並集大小可以表示他們的相似長度。

實現原理

內部組成

HashSet內部是用HashMap實現的,它內部有一個HashMap執行個體變數,如下所示:

private transient HashMap<E,Object> map;

我們知道,Map有鍵和值,HashSet相當於只有鍵,值都是相同的固定值,這個值的定義為:

private static final Object PRESENT = new Object();

理解了這個內部組成,它的實現方法也就比較容易理解了,我們來看下代碼。

構造方法

HashSet的構造方法,主要就是調用了對應的HashMap的構造方法,比如:

public HashSet(int initialCapacity, float loadFactor) {    map = new HashMap<>(initialCapacity, loadFactor);}public HashSet(int initialCapacity) {    map = new HashMap<>(initialCapacity);}public HashSet() {    map = new HashMap<>();}

接受Collection參數的構造方法稍微不一樣,代碼為:

public HashSet(Collection<? extends E> c) {    map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));    addAll(c);}

也很容易理解,c.size()/.75f用於計算initialCapacity,0.75f是loadFactor的預設值。

添加元素

我們看add方法的代碼:

public boolean add(E e) {    return map.put(e, PRESENT)==null;}

就是調用map的put方法,元素e用於鍵,值就是那個固定值PRESENT,put返回null表示原來沒有對應的鍵,添加成功了。HashMap中一個鍵只會儲存一份,所以重複添加HashMap不會變化。

檢查是否包含元素

代碼為:

public boolean contains(Object o) {    return map.containsKey(o);}

就是檢查map中是否包含對應的鍵。

刪除元素

代碼為:

public boolean remove(Object o) {    return map.remove(o)==PRESENT;}

就是調用map的remove方法,傳回值為PRESENT表示原來有對應的鍵且刪除成功了。

迭代器

代碼為:

public Iterator<E> iterator() {    return map.keySet().iterator();}

就是返回map的keySet的迭代器。

HashSet特點分析

HashSet實現了Set介面,內部是通過HashMap實現的,這決定了它有如下特點:

  • 沒有重複元素
  • 可以高效的添加、刪除元素、判斷元素是否存在,效率都為O(1)。
  • 沒有順序

如果需求正好符合這些特點,那HashSet就是一個理想的選擇。

小結

本節介紹了HashSet的用法和實現原理,它實現了Set介面,不含重複元素,內部實現利用了HashMap,可以方便高效地實現如去重、集合運算等功能。

同HashMap一樣,HashSet沒有順序,如果要保持添加的順序,可以使用HashSet的一個子類LinkedHashSet。Set還有一個重要的實作類別,TreeSet,它可以排序。這兩個類,我們留待後續章節介紹。

HashMap和HashSet的共同實現機制是雜湊表,Map和Set還有一個重要的共同實現機制,樹,實作類別分別是TreeMap和TreeSet,讓我們在接下來的兩節中探討。

----------------

未完待續,查看最新文章,敬請關注公眾號“老馬說編程”(掃描下方二維碼),從入門到進階,深入淺出,老馬和你一起探索Java編程及電腦技術的本質。用心原創,保留所有著作權。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.