终于,我读懂了所有Java集合——set篇

举报
兔老大 发表于 2021/04/20 00:24:32 2021/04/20
【摘要】 HashSet (底层是HashMap) Set不允许元素重复。 基于HashMap实现,无容量限制。 是非线程安全的。 成员变量 private transient HashMap<E,Object> map; // Dummy value to associate with an Object in the backing Map private ...

HashSet

(底层是HashMap)

Set不允许元素重复。

基于HashMap实现,无容量限制。

是非线程安全的。

成员变量

private transient HashMap<E,Object> map;

// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();

 

构造方法


  
  1. /**
  2.  * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
  3.  * default initial capacity (16) and load factor (0.75).
  4.  */
  5. public HashSet() {
  6.     map = new HashMap<>();
  7. }
  8. public HashSet(int initialCapacity) {
  9.     map = new HashMap<>(initialCapacity);
  10. }
  11. public HashSet(int initialCapacity, float loadFactor) {
  12.     map = new HashMap<>(initialCapacity, loadFactor);
  13. }

添加


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

删除


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

 

遍历


  
  1. public Iterator<E> iterator() {
  2.     return map.keySet().iterator();
  3. }

 

包含


  
  1. public boolean contains(Object o) {
  2.     return map.containsKey(o);
  3. }

 

TreeSet

(底层是TreeMap)

基于TreeMap实现,支持排序(自然排序 或者 根据创建TreeSet 时提供的 Comparator 进行排序)。

是非线程安全的。

 

成员变量

/**
 * The backing map.
 */

private transient NavigableMap<E,Object> m;

// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();

 

构造方法


  
  1. public TreeSet() {
  2.     this(new TreeMap<E,Object>());
  3. }
  4. public TreeSet(Comparator<? super E> comparator) {
  5.     this(new TreeMap<>(comparator));
  6. }

 

添加


  
  1. public boolean add(E e) {
  2.     return m.put(e, PRESENT)==null;
  3. }

删除


  
  1. public boolean remove(Object o) {
  2.     return m.remove(o)==PRESENT;
  3. }

遍历


  
  1. public Iterator<E> iterator() {
  2.     return m.navigableKeySet().iterator();
  3. }

包含


  
  1. public boolean contains(Object o) {
  2.     return m.containsKey(o);
  3. }

获取开头


  
  1. public E first() {
  2.     return m.firstKey();
  3. }

获取结尾


  
  1. public E last() {
  2.     return m.lastKey();
  3. }

子集


  
  1. public NavigableSet<E> subSet(E fromElement, boolean fromInclusive,
  2.                               E toElement,   boolean toInclusive) {
  3.     return new TreeSet<>(m.subMap(fromElement, fromInclusive,
  4.                                    toElement,   toInclusive));
  5. }

默认是含头不含尾


  
  1. public SortedSet<E> subSet(E fromElement, E toElement) {
  2.     return subSet(fromElement, true, toElement, false);
  3. }

LinkedHashSet

(继承自HashSet,底层是LinkedHashMap)

LinkedHashSet继承自HashSet,源码更少、更简单,唯一的区别是LinkedHashSet内部使用的是LinkHashMap。这样做的意义或者好处就是LinkedHashSet中的元素顺序是可以保证的,也就是说遍历序和插入序是一致的

类声明

public class LinkedHashSet<E>
    extends HashSet<E>
    implements Set<E>, Cloneable, java.io.Serializable {}

构造方法


  
  1. public LinkedHashSet(int initialCapacity, float loadFactor) {
  2.     super(initialCapacity, loadFactor, true);
  3. }
  4. /**
  5.  * Constructs a new, empty linked hash set with the specified initial
  6.  * capacity and the default load factor (0.75).
  7.  *
  8.  * @param   initialCapacity   the initial capacity of the LinkedHashSet
  9.  * @throws  IllegalArgumentException if the initial capacity is less
  10.  *              than zero
  11.  */
  12. public LinkedHashSet(int initialCapacity) {
  13.     super(initialCapacity, .75f, true);
  14. }
  15. /**
  16.  * Constructs a new, empty linked hash set with the default initial
  17.  * capacity (16) and load factor (0.75).
  18.  */
  19. public LinkedHashSet() {
  20.     super(16, .75f, true);
  21. }

 

super指的是HashSet的default访问级别的构造方法


  
  1. /**
  2.  * Constructs a new, empty linked hash set.  (This package private
  3.  * constructor is only used by LinkedHashSet.) The backing
  4.  * HashMap instance is a LinkedHashMap with the specified initial
  5.  * capacity and the specified load factor.
  6.  *
  7.  * @param      initialCapacity   the initial capacity of the hash map
  8.  * @param      loadFactor        the load factor of the hash map
  9.  * @param      dummy             ignored (distinguishes this
  10.  *             constructor from other int, float constructor.)
  11.  * @throws     IllegalArgumentException if the initial capacity is less
  12.  *             than zero, or if the load factor is nonpositive
  13.  */
  14. HashSet(int initialCapacity, float loadFactor, boolean dummy) {
  15.     map = new LinkedHashMap<>(initialCapacity, loadFactor);
  16. }

BitSet

(位集,底层是long数组,用于替代List<Boolean>)

BitSet是位操作的对象,值只有0或1即false和true,内部维护了一个long数组,初始只有一个long,所以BitSet最小的size是64(8个字节64个位,可以存储64个数字),当随着存储的元素越来越多,BitSet内部会动态扩充,最终内部是由N个long来存储,这些针对操作都是透明的。

默认情况下,BitSet的所有位都是false即0。

不是线程安全的。

用1位来表示一个数据是否出现过,0为没有出现过,1表示出现过。使用的时候既可根据某一个是否为0表示,此数是否出现过。

一个1GB的空间,有8*1024*1024*1024 = 8.58*10^9bit,也就是1GB的空间可以表示85亿多个数。

常见的应用是那些需要对海量数据进行一些统计工作的时候,比如日志分析、用户数统计等等,如统计40亿个数据中没有出现的数据,将40亿个不同数据进行排序,海量数据去重等等。

JDK选择long数组作为BitSet的内部存储结构是出于性能的考虑,因为BitSet提供and和or这种操作,需要对两个BitSet中的所有bit位做and或者or,实现的时候需要遍历所有的数组元素。使用long能够使得循环的次数降到最低,所以Java选择使用long数组作为BitSet的内部存储结构。

去重示例


  
  1. public static void containChars(String str) {
  2.     BitSet used = new BitSet();
  3.     for (int i = 0; i < str.length(); i++)
  4.         used.set(str.charAt(i)); // set bit for char 
  5.     StringBuilder sb = new StringBuilder();
  6.     sb.append("[");
  7.     int size = used.size();
  8.     for (int i = 0; i < size; i++) {
  9.         if (used.get(i)) {
  10.             sb.append((char) i);
  11.         }
  12.     }
  13.     sb.append("]");
  14.     System.out.println(sb.toString());
  15. }
  16. public static void main(String[] args) {
  17.     containChars("abcdfab");
  18. }

 [abcdf]

排序示例


  
  1. public static void sortArray(int[] array) {
  2.     BitSet bitSet = new BitSet(2 << 13);
  3.     // 虽然可以自动扩容,但尽量在构造时指定估算大小,默认为64 
  4.     System.out.println("BitSet size: " + bitSet.size());
  5.     for (int i = 0; i < array.length; i++) {
  6.         bitSet.set(array[i]);
  7.     }
  8.     //剔除重复数字后的元素个数 
  9.     int bitLen = bitSet.cardinality();
  10.     //进行排序,即把bit为true的元素复制到另一个数组 
  11.     int[] orderedArray = new int[bitLen];
  12.     int k = 0;
  13.     for (int i = bitSet.nextSetBit(0); i >= 0; i = bitSet.nextSetBit(i + 1)) {
  14.         orderedArray[k++] = i;
  15.     }
  16.     System.out.println("After ordering: ");
  17.     for (int i = 0; i < bitLen; i++) {
  18.         System.out.print(orderedArray[i] + "\t");
  19.     }
  20. }
  21. public static void main(String[] args) {
  22.     int[] array = new int[]{423, 700, 9999, 2323, 356, 6400, 1, 2, 3, 2, 2, 2, 2};
  23.     sortArray(array);
  24. }

BitSet size: 16384

After ordering:

1     2     3     356 423 700 2323      6400      9999

CopyOnWriteArraySet

(底层是CopyOnWriteArrayList)

基于CopyOnWriteArrayList实现,其唯一的不同是在add时调用的是CopyOnWriteArrayList的addIfAbsent方法。

在每次add的时候都要进行数组的遍历,因此其性能会略低于CopyOnWriteArrayList。

文章来源: fantianzuo.blog.csdn.net,作者:兔老大RabbitMQ,版权归原作者所有,如需转载,请联系作者。

原文链接:fantianzuo.blog.csdn.net/article/details/103336742

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。