`

集合框架 Set篇---HashSet、LinkedHashSet、TreeSet、CopyOnWriteArraySet、ConcurrentSkipList

 
阅读更多
Set
---------
1.HashSet

2.LinkedHashSet
3.TreeSet
4.CopyOnWriteArraySet
5.ConcurrentSkipListSet

---------------------HashSet-------------------------

HashSet实现了AbstractSet,由下面的HashSet源码容易看出,其内部依赖于HashMap的实现。其迭代顺序是不能保证的,元素存储是无序的。

public class HashSet<E> extends AbstractSet<E>  implements Set<E>, Cloneable, java.io.Serializable{
    /*----------成员变量-------------*/
   private transient HashMap<E,Object>map;//内部的实现依赖于HashMap 

    // 一个Object对象作为HashMap中value值的填充,该值对set来说是没有什么意义的
    private static final Object PRESENT = new Object();
    /*------------------------------*/

    /*----------构造方法-------------*/
    /**
     *创建一个空的集合,实际上创建一个空的HashMap
     */
    public HashSet() {
        map = new HashMap<E,Object>();
    }

    /**
     * 把集合作为set的初始化数据,并对对应的HashMap的容量进行指定
     */
    public HashSet(Collection<? extends E> c) {
       map = new HashMap<E,Object>(Math.max((int) (c.size()/.75f) + 1, 16));
       addAll(c);
    }

    /**
     * 指定初始容量及负载因子(这些概念可以参考HashMap的实现部分)
     */
    public HashSet(int initialCapacity, float loadFactor) {
       map = new HashMap<E,Object>(initialCapacity, loadFactor);
    }

    /**
     * 指定初始容量(默认负载因子为0.75)
     */
    public HashSet(int initialCapacity) {
       map = new HashMap<E,Object>(initialCapacity);
    }

    /**
     *创建一个以LinkedHashMap为实现的LinkedHashSet时,调用该构造方法
     */
    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
         map = newLinkedHashMap<E,Object>(initialCapacity, loadFactor);
    }
    /*------------------------------*/

    /**
     * 通过HashMap的keySet返回一个无序的迭代器Iterator
     */
    public Iterator<E> iterator() {
        return map.keySet().iterator();
    }

    /**
     * 集合中元素的个数 
     */
    public int size() {
        return map.size();
    }

    /**
     * 判断集合是否为空
     */
    public boolean isEmpty() {
         return map.isEmpty();
    }

    /**
     *是否包含某个元素
     */
    public boolean contains(Object o) {
        return map.containsKey(o);
    }

    /**
     * 添加元素的操作
     */
    public boolean add(E e) {
         return map.put(e, PRESENT)==null;
    }

    /**
     * 删除元素的操作
     */
    public boolean remove(Object o) {
        return map.remove(o)==PRESENT;
    }

    /**
     * 清空集合
     */
    public void clear() {
         map.clear();
    }

    /**
     * 返回一个集合的“浅拷贝”对象
     */
    public Object clone() {
          try {
              HashSet<E> newSet = (HashSet<E>) super.clone();
              newSet.map = (HashMap<E, Object>) map.clone();
              return newSet;
            } catch (CloneNotSupportedException e) {
                 throw new InternalError();
          }
    }

    /**
     * 持久化集合时的写操作
     */
    private void writeObject(java.io.ObjectOutputStream s)  throws java.io.IOException {
         // Write out any hidden serialization magic
         s.defaultWriteObject();

        // Write out HashMap capacity and load factor
        s.writeInt(map.capacity());
        s.writeFloat(map.loadFactor());

        // Write out size
        s.writeInt(map.size());

        // Write out all elements in the proper order.
       for (Iterator i=map.keySet().iterator(); i.hasNext(); )
              s.writeObject(i.next());
    }

    /**
     * 持久化集合时的读操作
     */
    private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException {
        // Read in any hidden serialization magic
         s.defaultReadObject();

        // Read in HashMap capacity and load factor and create backing HashMap
        int capacity = s.readInt();
        float loadFactor = s.readFloat();
        map = (((HashSet)this) instanceof LinkedHashSet ? new LinkedHashMap<E,Object>(capacity, loadFactor) :
               new HashMap<E,Object>(capacity, loadFactor));

        // Read in size
        int size = s.readInt();

       // Read in all elements in the proper order.
       for (int i=0; i<size; i++) {
            E e = (E) s.readObject();
            map.put(e, PRESENT);
        }
    }
}
---------------------LinkedHashSet-------------------------

//下面就是LinkedHashSet的主要实现,内部出了构造方法,没有其他的任何代码,LinkedHashSet是HashSet的实现,

//通过调用HashSet中的构造方法,其背后是依赖于LinkedHashMap实现的,所以可以保持集合的元素的插入的顺序。

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

    public LinkedHashSet(int initialCapacity, float loadFactor) {
        super(initialCapacity, loadFactor, true);
    }

    public LinkedHashSet(int initialCapacity) {
        super(initialCapacity, .75f, true);
    }

    public LinkedHashSet() {
        super(16, .75f, true);
    }

    public LinkedHashSet(Collection<? extends E> c) {
        super(Math.max(2*c.size(), 11), .75f, true);
        addAll(c);
    }
}
---------------------TreeSet-------------------------

//TreeSet,一个排序的Set集合,底层依赖于TreeMap的实现
public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>, Cloneable, java.io.Serializable{
    private transient NavigableMap<E,Object> m;         //TreeMap也实现了NavigableMap接口
    private static final Object PRESENT = new Object(); //一个虚拟的填充值用的对象(对set集合没有实际意义)

    /*--------------构造方法--------------*/
    TreeSet(NavigableMap<E,Object> m) {
        this.m = m;
    }
    public TreeSet() {
       this(new TreeMap<E,Object>());
    }
    public TreeSet(Comparator<? super E> comparator) {
       this(new TreeMap<E,Object>(comparator));
    }
    public TreeSet(Collection<? extends E> c) {
        this();
        addAll(c);
    }
    public TreeSet(SortedSet<E> s) {
        this(s.comparator());
        addAll(s);
    }
    /*------------------------------------*/
    /**
     * 返回升序(asc)排列集合的迭代器
     */
    public Iterator<E> iterator() {
        return m.navigableKeySet().iterator();
    }

    /**
     * 返回降序(desc)排列集合的迭代器
     */
    public Iterator<E> descendingIterator() {
        return m.descendingKeySet().iterator();
    }

    /**
     * 返回一个降序的Set集合
     */
    public NavigableSet<E> descendingSet() {
        return new TreeSet(m.descendingMap());
    }

    /**
     * 元素个数size
     */
    public int size() {
        return m.size();
    }

    /**
     * 判断是否为空集合
     */
    public boolean isEmpty() {
        return m.isEmpty();
    }

    /**
     * 是否包含某元素
     */
    public boolean contains(Object o) {
        return m.containsKey(o);
    }

    /**
     *添加元素
     */
    public boolean add(E e) {
        return m.put(e, PRESENT)==null;
    }

    /**
     *移除元素
     */
    public boolean remove(Object o) {
        return m.remove(o)==PRESENT;
    }

    /**
     * 清空集合
     */
    public void clear() {
        m.clear();
    }

    /**
     * 把给定的集合中的元素添加到set集合中(依赖于TreeMap的实现)
     */
    public  boolean addAll(Collection<? extends E> c) {
        // Use linear-time version if applicable
        if (m.size()==0 && c.size() > 0 && c instanceof SortedSet && m instanceof TreeMap) {
            SortedSet<? extends E> set = (SortedSet<? extends E>) c;
            TreeMap<E,Object> map = (TreeMap<E, Object>) m;
            Comparator<? super E> cc = (Comparator<? super E>) set.comparator();
            Comparator<? super E> mc = map.comparator();
            if (cc==mc || (cc != null && cc.equals(mc))) {
                map.addAllForTreeSet(set, PRESENT);
                return true;
            }
        }
        return super.addAll(c);
    }

    /*--以下是由于实现-NavigableSet和SortedSet接口而实现的一些方法,根据方法名很容易判断其作用,不再一一说明----*/
    public NavigableSet<E> subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) {
        return new TreeSet<E>(m.subMap(fromElement, fromInclusive, toElement, toInclusive));
    }

    public NavigableSet<E> headSet(E toElement, boolean inclusive) {
        return new TreeSet<E>(m.headMap(toElement, inclusive));
    }
    public NavigableSet<E> tailSet(E fromElement, boolean inclusive) {
        return new TreeSet<E>(m.tailMap(fromElement, inclusive));
    }
    public SortedSet<E> subSet(E fromElement, E toElement) {
        return subSet(fromElement, true, toElement, false);
    }
    public SortedSet<E> headSet(E toElement) {
        return headSet(toElement, false);
    }
    public SortedSet<E> tailSet(E fromElement) {
        return tailSet(fromElement, true);
    }

    public Comparator<? super E> comparator() {
        return m.comparator();
    }
    public E first() {
        return m.firstKey();
    }
    public E last() {
        return m.lastKey();
    }

    // NavigableSet API methods

    public E lower(E e) {
        return m.lowerKey(e);
    }
    public E floor(E e) {
        return m.floorKey(e);
    }
    public E ceiling(E e) {
        return m.ceilingKey(e);
    }

    public E higher(E e) {
        return m.higherKey(e);
    }
    public E pollFirst() {
        Map.Entry<E,?> e = m.pollFirstEntry();
        return (e == null)? null : e.getKey();
    }
    public E pollLast() {
        Map.Entry<E,?> e = m.pollLastEntry();
        return (e == null)? null : e.getKey();
    }
   
  //writeObject readObject等方法没有一一列出
}
--------------------CopyOnWriteArraySet----------------

CopyOnWriteArraySet是一个依赖CopyOnWriteArrayList实现的 Set集合。因此,它共享以下相同的基本属性:

-它最适合于具有以下特征的应用程序:set 大小通常保持很小,只读操作远多于可变操作,需要在遍历期间防止线程间的冲突。
-它是线程安全的。
-因为通常需要复制整个基础数组,所以可变操作(add、set 和 remove 等等)的开销很大。
-迭代器不支持可变 remove 操作。
-使用迭代器进行遍历的速度很快,并且不会与其他线程发生冲突。在构造迭代器时,迭代器依赖于不变的数组快照。

//CopyOnWriteArraySet的源码实现
public class CopyOnWriteArraySet<E> extends AbstractSet<E> implements java.io.Serializable {
    private final CopyOnWriteArrayList<E> al;//内部实现所依赖的CopyOnWriteArrayList对象

    /**
     * 空构造
     */
    public CopyOnWriteArraySet() {
        al = new CopyOnWriteArrayList<E>();
    }
    /**
     * 传入集合变量的构造
     */
    public CopyOnWriteArraySet(Collection<? extends E> c) {
        al = new CopyOnWriteArrayList<E>();
        al.addAllAbsent(c);
    }

    /**
     * 通过调用CopyOnWriteArrayList的方法,实现该Set集合的操作
     */
    public int size() {
         return al.size();
    }
    public boolean isEmpty() {
         return al.isEmpty();
    }
    public boolean contains(Object o) {
         return al.contains(o);
    }
    public Object[] toArray() {
         return al.toArray();
    }
    public <T> T[] toArray(T[] a) {
         return al.toArray(a);
    }
    public void clear() {
        al.clear();
    }
    public boolean remove(Object o) {
         return al.remove(o);
    }
    public boolean add(E e) {
         return al.addIfAbsent(e);//这就保证了Set集合不存在相同的元素
    }
    public boolean containsAll(Collection<?> c) {
         return al.containsAll(c);
    }
    public boolean addAll(Collection<? extends E> c) {
         return al.addAllAbsent(c) > 0;
    }
    public boolean removeAll(Collection<?> c) {
         return al.removeAll(c);
    }
    public boolean retainAll(Collection<?> c) {
         return al.retainAll(c);
    }
    public Iterator<E> iterator() {
         return al.iterator();
    }

    /**
     * 重写Set的equals方法:作用是比较两个set集合的是否相等
     */
    public boolean equals(Object o) {
        if (o == this) //如果是同一个set对象,就相等
            return true;
        if (!(o instanceof Set))//不是Set类别,不可能相等
            return false;
        Set<?> set = (Set<?>)(o);
         Iterator<?> it = set.iterator();

        // 下面的算法复杂度为O(n^2),所以CopyOnWriteArraySet过大的情况效率会比较低,不推荐。
        //  Use a single snapshot of underlying array
         Object[] elements = al.getArray();
         int len = elements.length;
        // Mark matched elements to avoid re-checking
        boolean[] matched = new boolean[len];
        int k = 0;
        //通过迭代比较每一个元素是否和al.getArray()获得的数组元素是否相同
        outer: while (it.hasNext()) {
            if (++k > len)
                return false;
            Object x = it.next();
            for (int i = 0; i < len; ++i) {
                if (!matched[i] && eq(x, elements[i])) {
                    matched[i] = true;
                    continue outer;
                }
            }
           return false;
        }
        return k == len;
    }

    /**
     * 考虑null的情况下,判断equals
     */
    private static boolean eq(Object o1, Object o2) {
        return (o1 == null ? o2 == null : o1.equals(o2));
    }
}

-------------------ConcurrentSkipListSet--------------------------

ConcurrentSkipListSet是一个基于 ConcurrentSkipListMap 的可缩放并发 NavigableSet 实现。set 的元素可以根据它们的自然顺序进行排序,也可以根据创建 set 时所提供的 Comparator 进行排序,具体取决于使用的构造方法。
此实现为 contains、add、remove 操作及其变体提供预期平均 log(n) 时间开销。多个线程可以安全地并发执行插入、移除和访问操作。
//ConcurrentSkipListSet的源码实现
public class ConcurrentSkipListSet<E> extends AbstractSet<E> implements NavigableSet<E>, Cloneable, java.io.Serializable {
    private final ConcurrentNavigableMap<E,Object> m;//基于 ConcurrentSkipListMap

    /*---------------------------构造方法-----------------------------*/
    public ConcurrentSkipListSet() {
        m = new ConcurrentSkipListMap<E,Object>();
    }

    public ConcurrentSkipListSet(Comparator<? super E> comparator) {
        m = new ConcurrentSkipListMap<E,Object>(comparator);
    }

    public ConcurrentSkipListSet(Collection<? extends E> c) {
        m = new ConcurrentSkipListMap<E,Object>();
        addAll(c);
    }

    public ConcurrentSkipListSet(SortedSet<E> s) {
        m = new ConcurrentSkipListMap<E,Object>(s.comparator());
        addAll(s);
    }

    ConcurrentSkipListSet(ConcurrentNavigableMap<E,Object> m) {
        this.m = m;
    }
    /*-----------------------------------------------------------------*/

    public ConcurrentSkipListSet<E> clone() {
        ConcurrentSkipListSet<E> clone = null;
         try {
             clone = (ConcurrentSkipListSet<E>) super.clone();
            clone.setMap(new ConcurrentSkipListMap(m));
         } catch (CloneNotSupportedException e) {
             throw new InternalError();
         }

        return clone;
    }

    /* ----------------基于 ConcurrentSkipListMap,实现该Set集合的操作  -------------- */
    public int size() {
         return m.size();
    }

    public boolean isEmpty() {
         return m.isEmpty();
    }

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

    public boolean add(E e) {
         return m.putIfAbsent(e, Boolean.TRUE) == null;
    }

    public boolean remove(Object o) {
         return m.remove(o, Boolean.TRUE);
    }
    public void clear() {
         m.clear();
    }
    public Iterator<E> iterator() {
        return m.navigableKeySet().iterator();
    }

    public Iterator<E> descendingIterator() {
         return m.descendingKeySet().iterator();
    }


    /* ---------------- 重写AbstractSet 的equals方法-------------- */

    public boolean equals(Object o) {
        // Override AbstractSet version to avoid calling size()
         if (o == this)
             return true;
         if (!(o instanceof Set))
             return false;
         Collection<?> c = (Collection<?>) o;
        try {
            return containsAll(c) && c.containsAll(this);
        } catch (ClassCastException unused)   {
            return false;
        } catch (NullPointerException unused) {
            return false;
        }
    }

    public boolean removeAll(Collection<?> c) {
        // Override AbstractSet version to avoid unnecessary call to size()
        boolean modified = false;
        for (Iterator<?> i = c.iterator(); i.hasNext(); )
            if (remove(i.next()))
                modified = true;
        return modified;
    }

    /* ---------------- 实现NavigableSet接口的操作-------------- */

    public E lower(E e) {
        return m.lowerKey(e);
    }
    public E floor(E e) {
        return m.floorKey(e);
    }
    public E ceiling(E e) {
        return m.ceilingKey(e);
    }
    public E higher(E e) {
        return m.higherKey(e);
    }

    public E pollFirst() {
        Map.Entry<E,Object> e = m.pollFirstEntry();
        return e == null? null : e.getKey();
    }

    public E pollLast() {
        Map.Entry<E,Object> e = m.pollLastEntry();
        return e == null? null : e.getKey();
    }


    /* ---------------- 实现SortedSet接口的操作 -------------- */

    public Comparator<? super E> comparator() {
        return m.comparator();
    }

    public E first() {
        return m.firstKey();
    }
    public E last() {
        return m.lastKey();
    }

    public NavigableSet<E> subSet(E fromElement,boolean fromInclusive, E toElement, boolean toInclusive) {
         return new ConcurrentSkipListSet<E>(m.subMap(fromElement, fromInclusive, toElement, toInclusive));
    }
    public NavigableSet<E> headSet(E toElement, boolean inclusive) {
         return new ConcurrentSkipListSet<E>(m.headMap(toElement, inclusive));
    }
    public NavigableSet<E> tailSet(E fromElement, boolean inclusive) {
         return new ConcurrentSkipListSet<E>(m.tailMap(fromElement, inclusive));
    }

    public NavigableSet<E> subSet(E fromElement, E toElement) {
         return subSet(fromElement, true, toElement, false);
    }

    public NavigableSet<E> headSet(E toElement) {
         return headSet(toElement, false);
    }
    public NavigableSet<E> tailSet(E fromElement) {
         return tailSet(fromElement, true);
    }

    public NavigableSet<E> descendingSet() {
         return new ConcurrentSkipListSet(m.descendingMap());
    }

    // Support for resetting map in clone
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long mapOffset;
    static {
        try {
            mapOffset = unsafe.objectFieldOffset(ConcurrentSkipListSet.class.getDeclaredField("m"));
        } catch (Exception ex) { throw new Error(ex); }
    }
    private void setMap(ConcurrentNavigableMap<E,Object> map) {
        unsafe.putObjectVolatile(this, mapOffset, map);
    }

}
分享到:
评论

相关推荐

    Java集合框架常见面试题.7z

    - **Set**:无序且不包含重复元素的集合,例如HashSet和TreeSet。 - **Queue**:遵循先进先出(FIFO)原则的数据结构,用于暂存和处理元素,例如LinkedList(作为队列使用)和PriorityQueue。 3. **ArrayList和...

    java 运用集的相关类(Set)

    Java提供了多种Set接口的实现,如HashSet、TreeSet和LinkedHashSet。 1. HashSet:这是最常用的Set实现,它不保证元素的顺序,允许使用null值,但不允许元素重复。HashSet内部使用哈希表来存储元素,因此添加、删除...

    java集合框架

    - `HashSet`、`LinkedHashSet` 和 `TreeSet`:`Set` 的实现,分别基于哈希表、有序链接表和红黑树。 - `HashMap`、`LinkedHashMap` 和 `TreeMap`:`Map` 的实现,对应哈希表、有序链接表和红黑树。 - `ArrayDeque...

    Java集合框架的知识总结.doc

    本篇文章将对Java集合框架进行深入的总结,包括其核心接口、类以及常用方法。 1. **集合框架概述** Java集合框架的核心接口主要有两个:`Collection`和`Map`。`Collection`接口是所有单值容器的基接口,如`List`、...

    对Java中Set的深入研究.pdf

    Set接口继承自Collection接口,并提供了多种实现类,如HashSet、LinkedHashSet、TreeSet和CopyOnWriteArraySet等。这些实现类各自有不同的特性和使用场景。 1. 实现类详解: - `CopyOnWriteArraySet`:这个类基于`...

    全面接触Java集合框架

    `HashSet`、`LinkedHashSet`和`TreeSet`是常见的实现。 - `Queue`:用于处理先进先出(FIFO)的数据结构,如`ArrayDeque`和`PriorityQueue`。 2. **类**: - `ArrayList`:基于动态数组实现的列表,提供快速随机...

    java中的集合笔记.rar

    集合框架主要包括List、Set、Queue和Map四大接口,以及一系列实现这些接口的类,如ArrayList、LinkedList、HashSet、HashMap等。这份"java中的集合笔记"涵盖了这些关键概念。 1. **集合接口** - **List**: List...

    Java基础学习25.pdf

    通过以上内容的介绍,读者应能更深入地理解Java集合框架中的Set接口及其各种实现的内部原理,并掌握如何在Java编程中使用这些集合来解决实际问题。同时,还应该对并发集合类有所了解,以及如何在多线程环境下安全地...

    对Java中Set的深入研究

    `Set`接口属于Java集合框架的一部分,继承自`Collection`接口,并在此基础上增加了对元素唯一性的限制。本篇文章将详细介绍`Set`接口的实现机制及其内部工作原理,包括如何判断元素是否重复、不同类型的`Set`实现类...

    九阳真经-java基础面试.docx

    - **LinkedHashSet**:在HashSet的基础上增加了链表结构,保证元素的插入顺序,性能介于HashSet和TreeSet之间。 - **EnumSet**:适用于存储枚举类型的Set,高性能。 - **CopyOnWriteArraySet**:使用COW(Copy-On...

    Java中Set的深入研究

    Set接口是Java集合框架的一部分,它代表了一个数学抽象集合,即不允许包含重复元素的集合。更正式地讲,根据其Javadoc文档,Set是一个不包含任何重复元素的集合。具体来说,Set不允许包含满足`e1.equals(e2)`条件的...

    数据结构面试专题.docx

    - **Set接口**:不包含重复元素,如HashSet、LinkedHashSet和TreeSet。 - **List接口**:有序集合,允许重复元素,支持索引访问,如ArrayList和LinkedList。 - **Queue接口**:继承自Collection,表示队列行为,...

    java集合总结副本共19页.pdf.zip

    这个“java集合总结副本共19页.pdf.zip”压缩包很可能是对Java集合框架的详细讲解,涵盖了重要的知识点,包括ArrayList、LinkedList、HashSet、HashMap、TreeSet、TreeMap等主要集合类,以及它们的特点、性能和应用...

    Java的集合体系练习

    Set则不允许有重复元素,如HashSet、LinkedHashSet和TreeSet。除此之外,Map接口是另一种重要的集合类型,它存储键值对,如HashMap、TreeMap和LinkedHashMap。 ArrayList是基于动态数组实现的,提供了快速的随机...

    Java基础----集合类汇总

    LinkedHashSet保持了元素的插入顺序,而TreeSet则按照元素的自然排序或自定义比较器进行排序。 Map接口则用于存储键值对,键(Key)必须是唯一的,而值(Value)可以重复。HashMap是Map接口的一个实现,它同样使用...

    深入学习java源码-Java-Collection-Framework:java集合框架详解,这里有集合框架的深入学习并且贴出了部分重要

    `HashSet`、`TreeSet`和`LinkedHashSet`是`Set`的不同实现。`HashSet`是最基础的,不保证元素顺序,基于`HashMap`实现;`TreeSet`内部使用`TreeMap`,保证元素排序,支持自然排序或定制排序;`LinkedHashSet`保持...

    2020-07-28课堂笔记.docx

    在Java编程语言中,集合框架是处理对象组的重要工具,其中Collection是最基础的接口。本堂课主要聚焦于Collection接口及其子接口,特别是Set接口及其常见的实现类。 首先,Collection接口是所有单值容器的父接口,...

    java集合,java集合

    在Java中,集合主要分为两大类:List和Set,而Map则不直接属于集合,但与集合框架紧密相关。 1. **List接口**:List是一种有序的集合,允许元素重复,并且可以按照索引访问。ArrayList和LinkedList是List接口的主要...

    Java面试问题2023集合

    2. **实现类**:每个接口都有若干个实现类,例如`ArrayList`和`LinkedList`实现了`List`接口,`HashSet`、`LinkedHashSet`和`TreeSet`实现了`Set`接口,`ArrayDeque`和`PriorityQueue`实现了`Queue`接口。...

Global site tag (gtag.js) - Google Analytics