• HashSet和CopyOnWriteArraySet


    前言

    这篇文章的目的如下:

    • HashSet是如何保证元素的不重复和无序
    • HashSet的增删(改查?)原理
    • CopyOnWriteArraySet支持并发的原理
    • CopyOnWriteArraySet的增删(改查?)原理

    如果不想看分析过程,可直接拉到文章末尾看结论

    先来看看 Set接口

     1 public interface Set<E> extends Collection<E> {
     2  
     3     int size();
     4     boolean isEmpty();
     5     boolean contains(Object o);
     6     Object[] toArray();
     7     <T> T[] toArray(T[] a);
     8     boolean add(E e);
     9     boolean remove(Object o);
    10     boolean containsAll(Collection<?> c);
    11     boolean addAll(Collection<? extends E> c);
    12     boolean retainAll(Collection<?> c);
    13     boolean removeAll(Collection<?> c);
    14     boolean equals(Object o);
    15     int hashCode();
    16 }

    我们从以上接口发现Set并没有get和set方法,也就是没有查和改,为什么呢?原因如下:

    • 因为Set是无序的,没有通过index来进行查询
    • 同样是因为Set是无序的,也就是没有办法通过Index来进行修改

    1 HashSet如何保证元素不重复?

    要弄清楚HashSet如何保证里面的元素不重复,得从以下两个方面入手:

    • 它底层的存储结构是什么?
    • 插入时是如何判断元素是否存在?

    当我们弄清楚上面两个问题之后我们也可以明白HashSet为什么是无序的了。

    1)HashSet的底层存储逻辑

    且看源码:

     1 public class HashSet<E>
     2     extends AbstractSet<E>
     3     implements Set<E>, Cloneable, java.io.Serializable{
     4     static final long serialVersionUID = -5024744406713321676L;
     5 
     6     private transient HashMap<E,Object> map;
     7 
     8     // Dummy value to associate with an Object in the backing Map
     9     private static final Object PRESENT = new Object();
    10 
    11     /**
    12      * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
    13      * default initial capacity (16) and load factor (0.75).
    14      */
    15     public HashSet() {
    16         map = new HashMap<>();
    17     }
    18     ...
    19 }

    我们可以看出来HashSet的底层存储结构是一个HashMap,并且HashSet的元素作为该Map的Key进行存储,HashMap的Key的存储是无序并且不可重复,这就解释了HashSet中如何保证元素不重复

    2)插入逻辑

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

    直接put到map当中

    3)总结

    由以上内容我们可以知道HashSet的底层存储结构是HashMap,并且插入到HashSet中元素作为map的key进行存储,这就保证HashSet的一下特点:

    • HashSet中的元素不重复的
    • HashSet中的元素是无序的

    2 HashSet增删(改查?)原理

    我们从上一小节了解到HashSet的底层存储结构是HashMap,那么它的增删也就是map的put和remove

    1)增

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

    直接put到map当中

    2)删

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

    直接在map中移除即可,非常简单

    3 CopyOnWriteArraySet为什么能支持并发?

    在搞清楚CopyOnWriteArraySet为什么能支持并发 这个问题之前,我们先来想想以下几个问题:

    • HashSet对应的并发类为什么叫CopyOnWriteArraySet,而不是叫CopyOnWriteHashSet呢?
    • CopyOnWriteArraySet和CopyOnWriteArrayList有没有关系?

    我想一旦我们弄清楚上面两个问题我们就是知道 CopyOnWriteArraySet为什么能支持并发?

    先来看看CopyOnWriteArraySet的部分源码:

     1 public class CopyOnWriteArraySet<E> extends AbstractSet<E>
     2         implements java.io.Serializable {
     3     private static final long serialVersionUID = 5457747651344034263L;
     4 
     5     private final CopyOnWriteArrayList<E> al;
     6 
     7     /**
     8      * Creates an empty set.
     9      */
    10     public CopyOnWriteArraySet() {
    11         al = new CopyOnWriteArrayList<E>();
    12     }
    13     ...
    14 }

    从源码中神奇地发现CopyOnWriteArraySet的底层存储结构竟然是CopyOnWriteArrayList,那么我们就可以知道它的名字的由来了,并且知道它支持并发的原理跟CopyOnWriteArrayList是一样的。

    附:CopyOnWriteArrayList原理解析

    4 CopyOnWriteArraySet的增删(改查?)原理

    1)增

    public boolean add(E e) {
        return al.addIfAbsent(e);
    }

    看方法名我们就是如果CopyOnWriteArrayList中不存在某元素才会添加成功

    2)删

    public boolean remove(Object o) {
        return al.remove(o);
    }

    直接从CopyOnWriteArrayList中移除

    5 总结

    • HashSet是如何保证元素的不重复和无序

      答:因为HashSet的底层存储结构是HashMap,并且HashSet中的元素是作为Map的Key存储到Map中,所以HashMap中Key是不重复且无序,所以HashSet中的元素也就是不重复和无序的

    • HashSet的增删(改查?)原理

      HashSet的增删原理很简单,就是map的put和remove,为什么没有改查呢?那是因为HashSet中的元素是无序的,没办法根据索引进行查询和修改

    • CopyOnWriteArraySet支持并发的原理

      CopyOnWriteArraySet之所以叫CopyOnWriteArraySet,是因为它的底层存储结构是CopyOnWriteArrayList,同时也就是保证了它的并发安全性

    • CopyOnWriteArraySet的增删(改查?)原理

      CopyOnWriteArraySet继承了AbstractSet,跟HashSet一样只有增删,没有改查,增删原理也就是调用CopyOnWriteArrayList的增删方法,只不过增的时候需要判断一下List中是否存储该元素

  • 相关阅读:
    一个十年java程序员的心得
    程序员每天应该思考的5个问题,你有思考过吗?
    Java---Java的面试题(二)
    Java---Java的面试题(一)
    java中的几种取整函数
    mysql ,为什么一张表的varchar关联另一张表varchar执行失败的一个原因
    可注册两位字母+两位数字com域名大全(到2016-5-12:12时候)
    更改(修改)mysql自动增序列改变从1000开始
    HTTP状态码(HTTP Status Code)
    struts2、jsp的简单路径的简单拦截
  • 原文地址:https://www.cnblogs.com/lgjava/p/11731068.html
Copyright © 2020-2023  润新知