zoukankan      html  css  js  c++  java
  • Java 集合系列 16 HashSet

    java 集合系列目录:

    Java 集合系列 01 总体框架

    Java 集合系列 02 Collection架构

    Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例

    Java 集合系列 04 LinkedList详细介绍(源码解析)和使用示例 

    Java 集合系列 05 Vector详细介绍(源码解析)和使用示例

    Java 集合系列 06 Stack详细介绍(源码解析)和使用示例

    Java 集合系列 07 List总结(LinkedList, ArrayList等使用场景和性能分析)

    Java 集合系列 08 Map架构

    Java 集合系列 09 HashMap详细介绍(源码解析)和使用示例

    Java 集合系列 10 Hashtable详细介绍(源码解析)和使用示例

    Java 集合系列 11 hashmap 和 hashtable 的区别

    Java 集合系列 12 TreeMap

    Java 集合系列 13 WeakHashMap

    Java 集合系列 14 hashCode

    Java 集合系列 15 Map总结

    Java 集合系列 16 HashSet

    Java 集合系列 17 TreeSet

    概述

    第1 部分 HashSet介绍 

      1.1 HashSet 简介

      1.2 HashSet的构造函数

      1.3 HashSet的主要API

    第2 部分 HashSet数据结构

    第3 部分 源码分析

    第1 部分 HashSet介绍

    1.1 HashSet 简介

      HashSet 是一个没有重复元素的集合。它是由HashMap实现的,不保证元素的顺序,而且HashSet允许使用 null 元素

    HashSet是非同步的。如果多个线程同时访问一个哈希 set,而其中至少一个线程修改了该 set,那么它必须 保持外部同步。这通常是通过对自然封装该 set 的对象执行同步操作来完成的。如果不存在这样的对象,则应该使用 Collections.synchronizedSet 方法来“包装” set。最好在创建时完成这一操作,以防止对该 set 进行意外的不同步访问:

    Set s = Collections.synchronizedSet(new HashSet(...));

    1.2 HashSet的构造函数

    // 默认构造函数
    public HashSet() 
    
    // 带集合的构造函数
    public HashSet(Collection<? extends E> c) 
    
    // 指定HashSet初始容量和加载因子的构造函数
    public HashSet(int initialCapacity, float loadFactor) 
    
    // 指定HashSet初始容量的构造函数
    public HashSet(int initialCapacity) 
    
    // 指定HashSet初始容量和加载因子的构造函数,dummy没有任何作用
    HashSet(int initialCapacity, float loadFactor, boolean dummy)

    1.3 HashSet的主要API

     boolean add(E e) 
              如果此 set 中尚未包含指定元素,则添加指定元素。 
     void clear() 
              从此 set 中移除所有元素。 
     Object clone() 
              返回此 HashSet 实例的浅表副本:并没有复制这些元素本身。 
     boolean contains(Object o) 
              如果此 set 包含指定元素,则返回 trueboolean isEmpty() 
              如果此 set 不包含任何元素,则返回 true。 
     Iterator<E> iterator() 
              返回对此 set 中元素进行迭代的迭代器。 
     boolean remove(Object o) 
              如果指定元素存在于此 set 中,则将其移除。 
     int size() 
              返回此 set 中的元素的数量(set 的容量)。 

    第2 部分 HashSet数据结构

    HashSet的继承关系如下:

    java.lang.Object
       ↳     java.util.AbstractCollection<E>
             ↳     java.util.AbstractSet<E>
                   ↳     java.util.HashSet<E>
    
    public class HashSet<E>
        extends AbstractSet<E>
        implements Set<E>, Cloneable, java.io.Serializable { }

    HashSet与Map关系如下图:

    从图中可以看出:
    (01) HashSet继承于AbstractSet,并且实现了Set接口。
    (02) HashSet的本质是一个"没有重复元素"的集合,它是通过HashMap实现的。HashSet中含有一个"HashMap类型的成员变量"map,HashSet的操作函数,实际上都是通过map实现的。

    第3 部分 源码分析

      1 public class HashSet<E>
      2     extends AbstractSet<E>
      3     implements Set<E>, Cloneable, java.io.Serializable
      4 {
      5     static final long serialVersionUID = -5024744406713321676L;
      6 
      7         // HashSet是通过map(HashMap对象)保存内容的
      8     private transient HashMap<E,Object> map;
      9 
     10     // PRESENT是向map中插入key-value对应的value
     11     // 因为HashSet中只需要用到key,而HashMap是key-value键值对;
     12     // 所以,向map中添加键值对时,键值对的值固定是PRESENT
     13     private static final Object PRESENT = new Object();
     14 
     15      // 默认构造函数
     16      //default initial capacity (16) and load factor (0.75)
     17     public HashSet() {
     18             // 调用HashMap的默认构造函数,创建map
     19         map = new HashMap<>();
     20     }
     21 
     22     // 带集合的构造函数
     23     public HashSet(Collection<? extends E> c) {
     24             // 创建map。
     25         // 为什么要调用Math.max((int) (c.size()/.75f) + 1, 16),从 (c.size()/.75f) + 1 和 16 中选择一个比较大的树呢?        
     26         // 首先,说明(c.size()/.75f) + 1
     27         //   因为从HashMap的效率(时间成本和空间成本)考虑,HashMap的加载因子是0.75。
     28         //   当HashMap的“阈值”(阈值=HashMap总的大小*加载因子) < “HashMap实际大小”时,
     29         //   就需要将HashMap的容量翻倍。
     30         //   所以,(c.size()/.75f) + 1 计算出来的正好是总的空间大小。
     31         // 接下来,说明为什么是 16 。
     32         //   HashMap的总的大小,必须是2的指数倍。若创建HashMap时,指定的大小不是2的指数倍;
     33         //   HashMap的构造函数中也会重新计算,找出比“指定大小”大的最小的2的指数倍的数。
     34         //   所以,这里指定为16是从性能考虑。避免重复计算。
     35         map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
     36         // 将集合(c)中的全部元素添加到HashSet中
     37         addAll(c);
     38     }
     39 
     40     // 指定HashSet初始容量和加载因子的构造函数
     41     public HashSet(int initialCapacity, float loadFactor) {
     42         map = new HashMap<>(initialCapacity, loadFactor);
     43     }
     44 
     45     // 指定HashSet初始容量的构造函数
     46     public HashSet(int initialCapacity) {
     47         map = new HashMap<>(initialCapacity);
     48     }
     49 
     50     /**
     51      * Constructs a new, empty linked hash set.  (This package private
     52      * constructor is only used by LinkedHashSet.) The backing
     53      * HashMap instance is a LinkedHashMap with the specified initial
     54      * capacity and the specified load factor.
     55      *
     56      * @param      initialCapacity   the initial capacity of the hash map
     57      * @param      loadFactor        the load factor of the hash map
     58      * @param      dummy             ignored (distinguishes this
     59      *             constructor from other int, float constructor.)
     60      * @throws     IllegalArgumentException if the initial capacity is less
     61      *             than zero, or if the load factor is nonpositive
     62      */
     63      //以指定的initialCapacity和loadFactor构造一个新的空链接哈希集合。
     64      //dummy 为标识 该构造函数主要作用是对LinkedHashSet起到一个支持作用
     65     HashSet(int initialCapacity, float loadFactor, boolean dummy) {
     66         map = new LinkedHashMap<>(initialCapacity, loadFactor);
     67     }
     68 
     69     // 返回HashSet的迭代器
     70     public Iterator<E> iterator() {
     71         return map.keySet().iterator();
     72     }
     73 
     74     public int size() {
     75         return map.size();
     76     }
     77 
     78     public boolean isEmpty() {
     79         return map.isEmpty();
     80     }
     81 
     82     public boolean contains(Object o) {
     83         return map.containsKey(o);
     84     }
     85 
     86     // 将元素(e)添加到HashSet中
     87     public boolean add(E e) {
     88         return map.put(e, PRESENT)==null;
     89     }
     90 
     91     // 删除HashSet中的元素(o)
     92     public boolean remove(Object o) {
     93         return map.remove(o)==PRESENT;
     94     }
     95 
     96     public void clear() {
     97         map.clear();
     98     }
     99 
    100     // 克隆一个HashSet,并返回Object对象
    101     public Object clone() {
    102         try {
    103             HashSet<E> newSet = (HashSet<E>) super.clone();
    104             newSet.map = (HashMap<E, Object>) map.clone();
    105             return newSet;
    106         } catch (CloneNotSupportedException e) {
    107             throw new InternalError();
    108         }
    109     }
    110 
    111     // java.io.Serializable的写入函数
    112     // 将HashSet的“总的容量,加载因子,实际容量,所有的元素”都写入到输出流中
    113     private void writeObject(java.io.ObjectOutputStream s)
    114         throws java.io.IOException {
    115         // Write out any hidden serialization magic
    116         s.defaultWriteObject();
    117 
    118         // Write out HashMap capacity and load factor
    119         s.writeInt(map.capacity());
    120         s.writeFloat(map.loadFactor());
    121 
    122         // Write out size
    123         s.writeInt(map.size());
    124 
    125         // Write out all elements in the proper order.
    126         for (E e : map.keySet())
    127             s.writeObject(e);
    128     }
    129 
    130     // java.io.Serializable的读取函数
    131     // 将HashSet的“总的容量,加载因子,实际容量,所有的元素”依次读出
    132     private void readObject(java.io.ObjectInputStream s)
    133         throws java.io.IOException, ClassNotFoundException {
    134         // Read in any hidden serialization magic
    135         s.defaultReadObject();
    136 
    137         // Read in HashMap capacity and load factor and create backing HashMap
    138         int capacity = s.readInt();
    139         float loadFactor = s.readFloat();
    140         map = (((HashSet)this) instanceof LinkedHashSet ?
    141                new LinkedHashMap<E,Object>(capacity, loadFactor) :
    142                new HashMap<E,Object>(capacity, loadFactor));
    143 
    144         // Read in size
    145         int size = s.readInt();
    146 
    147         // Read in all elements in the proper order.
    148         for (int i=0; i<size; i++) {
    149             E e = (E) s.readObject();
    150             map.put(e, PRESENT);
    151         }
    152     }
    153 }
  • 相关阅读:
    [LeetCode] 394. Decode String 解码字符串
    [LeetCode] 393. UTF-8 Validation 编码验证
    [LeetCode] Is Subsequence 是子序列
    [LintCode] Maximum Gap 求最大间距
    [LintCode] Nuts & Bolts Problem 螺栓螺母问题
    [LintCode] Kth Smallest Number in Sorted Matrix 有序矩阵中第K小的数字
    [LeetCode] Perfect Rectangle 完美矩形
    LaTex Remove Left Margin 去除左边空间
    LaTex Font Size 字体大小命令
    [LintCode] Continuous Subarray Sum 连续子数组之和
  • 原文地址:https://www.cnblogs.com/xingele0917/p/3939108.html
Copyright © 2011-2022 走看看