HashSet和CopyOnWriteArraySet
Posted lgjava
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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是一样的。
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中是否存储该元素
以上是关于HashSet和CopyOnWriteArraySet的主要内容,如果未能解决你的问题,请参考以下文章