您好,登錄后才能下訂單哦!
前言
Set 表示由無重復對象組成的集合,也是集合框架中重要的一種集合類型,直接擴展自 Collection 接口。在一個 Set 中,不能有兩個引用指向同一個對象,或兩個指向 null 的引用。如果對象 a 和 b 的引用滿足條件 a.equals(b),那么這兩個對象也不能同時出現在集合中。
通常 Set 是不要求元素有序的,但也有一些有序的實現,如 SortedMap 接口、LinkedHashSet 接口等。
概述
Set 的具體實現通常都是基于 Map 的。因為 Map 中鍵是唯一的,因而在基于 Map 實現 Set 時,只需要關心 Map 中的鍵,和鍵關聯的值不需要有意義,使用一個任意的對象“占位”即可。我們在前面分析 Map 中的迭代器時,KeySet() 方法得到的就是一個 Set。
前面我們分析過 Map 接口的幾個具體實現,通用的實現 HahsMap ,插入或訪問序的 LinkedHashMap , 按照鍵升序的 TreeMap。同樣,在 Set 的具體實現中,也有 HashSet 、 LinkedHashSet 和 TreeSet 等,分別和 Map 一一對應,它們的特性對應著相應的 Map 實現的特性。下面基于 HashSet 的實現做一個簡略的介紹。
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; // Dummy value to associate with an Object in the backing Map private static final Object PRESENT = new Object(); public HashSet() { map = new HashMap<>(); } public HashSet(Collection<? extends E> c) { map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16)); addAll(c); } public HashSet(int initialCapacity, float loadFactor) { map = new HashMap<>(initialCapacity, loadFactor); } public HashSet(int initialCapacity) { map = new HashMap<>(initialCapacity); } HashSet(int initialCapacity, float loadFactor, boolean dummy) { map = new LinkedHashMap<>(initialCapacity, loadFactor); } }
從成員變量和構造方法可以清楚地看到,內部使用了一個 HahsMap,同時定義了一個無意義的空的靜態 Object 對象(占用8byte) PRESENT。既然 map 中和鍵關聯的值沒有意義,為什么不干脆使用 null 呢?我們看一下 add() 方法:
public boolean add(E e) { return map.put(e, PRESENT)==null; }
Map 的 put() 方法在添加一個新的鍵時會返回 null,在更新一個已經存在的鍵關聯的值時會返回舊值。因而 Set 中的 add() 方法可以據此判斷新加入的元素是否改變了集合,如果改變了就返回 true。因而 PRESENT 不可以使用 null 。
其它的方法這里簡單地列一下,都是基于 map 實現的:
public boolean contains(Object o) { return map.containsKey(o); } public boolean remove(Object o) { return map.remove(o)==PRESENT; } public Iterator<E> iterator() { return map.keySet().iterator(); } public int size() { return map.size(); } public boolean isEmpty() { return map.isEmpty(); } public void clear() { map.clear(); } @SuppressWarnings("unchecked") public Object clone() { try { HashSet<E> newSet = (HashSet<E>) super.clone(); newSet.map = (HashMap<E, Object>) map.clone(); return newSet; } catch (CloneNotSupportedException e) { throw new InternalError(e); } } //序列化 private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { // Write out any hidden serialization magic s.defaultWriteObject(); // Write out HashMap capacity and load factor s.writeInt(map.capacity()); s.writeFloat(map.loadFactor()); // Write out size s.writeInt(map.size()); // Write out all elements in the proper order. for (E e : map.keySet()) s.writeObject(e); } private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { // Read in any hidden serialization magic s.defaultReadObject(); // Read capacity and verify non-negative. int capacity = s.readInt(); if (capacity < 0) { throw new InvalidObjectException("Illegal capacity: " + capacity); } // Read load factor and verify positive and non NaN. float loadFactor = s.readFloat(); if (loadFactor <= 0 || Float.isNaN(loadFactor)) { throw new InvalidObjectException("Illegal load factor: " + loadFactor); } // Read size and verify non-negative. int size = s.readInt(); if (size < 0) { throw new InvalidObjectException("Illegal size: " + size); } // Set the capacity according to the size and load factor ensuring that // the HashMap is at least 25% full but clamping to maximum capacity. capacity = (int) Math.min(size * Math.min(1 / loadFactor, 4.0f), HashMap.MAXIMUM_CAPACITY); // Create backing HashMap map = (((HashSet<?>)this) instanceof LinkedHashSet ? new LinkedHashMap<E,Object>(capacity, loadFactor) : new HashMap<E,Object>(capacity, loadFactor)); // Read in all elements in the proper order. for (int i=0; i<size; i++) { @SuppressWarnings("unchecked") E e = (E) s.readObject(); map.put(e, PRESENT); } }
小結
Set 的內部通常是基于 Map 來實現的,Map 中的 Key 構成了 Set,而 Value 全部使用一個無意義的 Object 。
Set 的特征與其內部的 Set 的特征是一致的。基于 HashMap 的 HashSet 是無序時的最佳通用實現,基于 LinkedHashMap 的 LinkedHashSet 保留插入或訪問的順序,基于 TreeMap 的 TreeSet 可以按照元素升序排列,要求元素實現 Comaprable 接口或自定義比較器。
HashSet , LinkedHashSet, TreeSet 都不是線程安全的,在多線程環境下使用時要注意同步問題。
CopyOnWriteArraySet 是一個線程安全的實現,但是并不是基于 Map 實現的,而是通過 CopyOnWriteArrayList 實現的。使用 addIfAbsent() 方法進行去重,性能比較一般。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。