JDK 8 HashSet 源码解析

【本文是为了梳理知识的总结性文章,总结了一些自认为相关的重要知识点,只为巩固记忆以及技术交流,忘批评指正。其中参考了很多前辈的文章,包括图片也是引用,如有冒犯,侵删。】

目录

0 存储结构

1 类定义

2 静态常量

3 属性

4 构造函数

默认构造方法

指定初始容量和负载因子的构造方法

指定初始容量的构造方法

使用LinkedHashMap的构造器

使用指定容器构造HashSet的构造器

5 常用方法

最佳实践


0 存储结构

从底层实现来看,HashSet 调用了HashMap来存储元素,不过之用到了Key,Value则用一个固定对象替代。由于Map中的key是不可重复的,这和Set不存在重复元素的特点刚好契合。使用HashMap也意味着不保证元素的顺序允许使用 null 元素。

1 类定义

public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable

2 静态常量

由于HashSet只需要使用Key进行存储,因此Value存储的是一个虚拟值。

private static final Object PRESENT = new Object();

3 属性

底层调用HashMap对象进行存储和操作。

private transient HashMap<E,Object> map;

4 构造函数

主要是初始化HashMap,设置负载因子和初始容量。

默认构造方法

使用默认的HashMap构造方法,负载因子0.75,初始容量16。

    public HashSet() {
        map = new HashMap<>();
    }

指定初始容量和负载因子的构造方法

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

指定初始容量的构造方法

使用默认负载因子0.75

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

使用LinkedHashMap的构造器

底层使用LinkedHashMap,并且是包私有的,只供LinkedHashMap使用。

    // dummy只用于区分构造方法
    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }

使用指定容器构造HashSet的构造器

    public HashSet(Collection<? extends E> c) {
        map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
        // 使用Collection实现的Iterator迭代器,将集合c的元素一个个加入HashSet中
        addAll(c);
    }

5 常用方法

底层的方法都是通过调用HashMap完成的代码都很简单,详情可以查看HashMap的代码JDK 8 HashMap 源码解析

   /** 
    * 返回set中元素的迭代器。调用hashMap的keySet来返回所有的key。 
    */  
   public Iterator<E> iterator() {  
       return map.keySet().iterator();  
   }  
 
   /** 
    * 返回set中的元素的数量。调用HashMap的size()方法数量。 
    */  
   public int size() {  
       return map.size();  
   }  
 
   /** 
    * 判断set是否为空。调用HashMap的isEmpty()进行判断。 
    */  
   public boolean isEmpty() {  
       return map.isEmpty();  
   }  
 
   /** 
    * 判断set包含指定元素。底层实际调用HashMap的containsKey来判断
    */  
   public boolean contains(Object o) {  
       return map.containsKey(o);  
   }  
 
   /** 
    * 添加元素。如果set已包含该元素,则该调用不更改set并返回false。 
    */  
   public boolean add(E e) {  
       return map.put(e, PRESENT)==null;  
   }  
 
   /** 
    * 移除set中的指定元素。 如果此set已包含该元素,则返回true。 
    * 调用HashMap的remove方法删除指定Entry。 
    */  
   public boolean remove(Object o) {  
       return map.remove(o)==PRESENT;  
   }  
 
   /** 
    * 清空set中移除所有元素。调用HashMap的clear方法清空。 
    */  
   public void clear() {  
       map.clear();  
   }  

最佳实践

  1. 由于底层使用HashMap实现的,所以最好预估使用场景所需的容量,并设置为初始值,防止多次扩容,影响性能(尤其在数据量大的时候);
  2. 存储自定义对象的时候,重写equals方法和hashCode方法,以免出错;
  3. HashMap不是线程安全的,多线程环境下可以使用Collections.synchronizedSet()进行包装;

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值