Java集合类学习--Set

23 篇文章 0 订阅

Set

JDK对Set的实现进行了取巧。我们都知道Set不允许出现相同的对象,而Map也同样不允许有两个相同的Key(出现相同的时候,就执行更新操作)。所以Set里的实现实际上是调用了对应的Map,将Set的存放的对象作为Map的Key。
Set接口的方法如下:
在这里插入图片描述
由此可以看出,Set基本上就保持了Collection的原方法,所以,就从Set的实现开始学习。

Set的实现主要有HashSetTreeSet

HashSet

HashSet源码中,属性包含两个信息点:

	// 内部使用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();

观察源码,我们知道HashSet的数据是存储在HashMap的实例对象map中的,并且对应于map中的key,而Object类型的引用PRESENT则是对应于map中的value的一个虚拟值,没有实际意义。联想到HashMap的一些特性:无序存储、key值唯一等等,我们就可以很自然地理解Set集合元素不能重复以及HashSet无序存储的特性了。(关于HashMap的具体实现后续学习补充。)
构造器(四种)
1.HashSet() 空的构造器,初始化一个空的HashMap

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

2.HashSet(Collection<? extends E> c) 传入一个子集c,用于初始化HashMap

public HashSet(Collection<? extends E> c) {
        map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
        addAll(c);
    }

3.HashSet(int initialCapacity, float loadFactor) 初始化一个空的HashMap,并指定初始容量和加载因子

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

4.HashSet(int initialCapacity) 初始化一个空的HashMap,并指定初始容量

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

除了上面之外,还有个特殊的:

// 非public,主要是给LinkedHashSet使用的
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
    map = new LinkedHashMap<>(initialCapacity, loadFactor);
}

其他方法解析:

 //克隆
 @SuppressWarnings("unchecked")
    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(e);
        }
    }

    /**
     * Save the state of this <tt>HashSet</tt> instance to a stream (that is,
     * serialize it).
     *
     * @serialData The capacity of the backing <tt>HashMap</tt> instance
     *             (int), and its load factor (float) are emitted, followed by
     *             the size of the set (the number of elements it contains)
     *             (int), followed by all of its elements (each an Object) in
     *             no particular order.
     */
	//序列化写
    private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException {
        // Write out any hidden serialization magic
        s.defaultWriteObject();

        // 写出map的容量和装载因子
        s.writeInt(map.capacity());
        s.writeFloat(map.loadFactor());

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

        // Write out all elements in the proper order.
        // 遍历写出所有元素
        for (E e : map.keySet())
            s.writeObject(e);
    }

    /**
     * Reconstitute the <tt>HashSet</tt> instance from a stream (that is,
     * deserialize it).
     */
    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        // Read in any hidden serialization magic
        s.defaultReadObject();

        // Read capacity and verify non-negative.
        int capacity = s.readInt();
        if (capacity < 0) {
            throw new InvalidObjectException("Illegal capacity: " +
                                             capacity);
        }

        // Read load factor and verify positive and non NaN.
        //读入装载因子, 并检查不能小于等于0或者是NaN(Not a Number)
        // java.lang.Float.NaN = 0.0f / 0.0f;
        float loadFactor = s.readFloat();
        if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
            throw new InvalidObjectException("Illegal load factor: " +
                                             loadFactor);
        }

        // Read size and verify non-negative.
        int size = s.readInt();
        if (size < 0) {
            throw new InvalidObjectException("Illegal size: " +
                                             size);
        }

        // Set the capacity according to the size and load factor ensuring that
        // the HashMap is at least 25% full but clamping to maximum capacity.
        // 根据元素个数重新设置容量
        capacity = (int) Math.min(size * Math.min(1 / loadFactor, 4.0f),
                HashMap.MAXIMUM_CAPACITY);

        // Create backing HashMap
        map = (((HashSet<?>)this) instanceof LinkedHashSet ?
               new LinkedHashMap<E,Object>(capacity, loadFactor) :
               new HashMap<E,Object>(capacity, loadFactor));

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

TreeSet

TreeSet是SortedSet接口的唯一实现类。前面说过,TreeSet没有自己的数据结构而是通过TreeMap实现的,所以TreeSet也是基于红黑二叉树的一种存储结构,所以TreeSet不允许null对象,并且是有序存储的(默认升序)。
属性:

	private transient NavigableMap<E,Object> m;

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

NavigableMap是继承自SrotedMap的一个接口,其实现类为TreeMap,所以说TreeMap实现的。

构造器(四种)

1.TreeSet() 空的构造器,初始化一个空的TreeMap,默认升序排列

	// 以自然排序方式创建一个新的 TreeMap,
    // 根据该 TreeSet 创建一个 TreeSet,
    // 使用该 TreeMap 的 key 来保存 Set 集合的元素
    public TreeSet() {
        this(new TreeMap<E,Object>());
    }

2.TreeSet(Comparator<? super E> comparator) 传入一个自定义的比较器,常常用于实现降序排列

      // 以定制排序方式创建一个新的 TreeMap,
     // 根据该 TreeSet 创建一个 TreeSet,
    // 使用该 TreeMap 的 key 来保存 Set 集合的元素
    public TreeSet(Comparator<? super E> comparator) {
        this(new TreeMap<>(comparator));
    }

3.TreeSet(Collection<? extends E> c) 传入一个子集c,用于初始化TreeMap对象,默认升序


    public TreeSet(Collection<? extends E> c) {
        this();
        // 向 TreeSet 中添加 Collection 集合 c 里的所有元素
        addAll(c);
    }

4.TreeSet(SortedSet s) 传入一个有序的子集s,用于初始化TreeMap对象,采用子集的比较器


   public TreeSet(SortedSet<E> s) {
        this(s.comparator());
        addAll(s);
    }

其他方法解析:
TreeSet 里绝大部分方法都是直接调用 TreeMap 的方法来实现的。(TreeMap后续学习补充 )


	// 逆序迭代器
    public Iterator<E> descendingIterator() {
        return m.descendingKeySet().iterator();
    }

     // 以逆序返回一个新的TreeSet
    public NavigableSet<E> descendingSet() {
        return new TreeSet<>(m.descendingMap());
    }
	 // 添加集合c中的所有元素
    public  boolean addAll(Collection<? extends E> c) {
        // 满足一定条件时直接调用TreeMap的addAllForTreeSet()方法添加元素
        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<?> cc = set.comparator();
            Comparator<? super E> mc = map.comparator();
            if (cc==mc || (cc != null && cc.equals(mc))) {
                map.addAllForTreeSet(set, PRESENT);
                return true;
            }
        }
        // 不满足上述条件, 调用父类的addAll()通过遍历的方式一个一个地添加元素
        return super.addAll(c);
    }

 // 子set(NavigableSet中的方法)
    public NavigableSet<E> subSet(E fromElement, boolean fromInclusive,
                                  E toElement,   boolean toInclusive) {
        return new TreeSet<>(m.subMap(fromElement, fromInclusive,
                                       toElement,   toInclusive));
    }
    
    // 头set(NavigableSet中的方法)
    public NavigableSet<E> headSet(E toElement, boolean inclusive) {
        return new TreeSet<>(m.headMap(toElement, inclusive));
    }

    // 尾set(NavigableSet中的方法)
    public NavigableSet<E> tailSet(E fromElement, boolean inclusive) {
        return new TreeSet<>(m.tailMap(fromElement, inclusive));
    }

    // 子set(SortedSet接口中的方法)
    public SortedSet<E> subSet(E fromElement, E toElement) {
        return subSet(fromElement, true, toElement, false);
    }

    // 头set(SortedSet接口中的方法)
    public SortedSet<E> headSet(E toElement) {
        return headSet(toElement, false);
    }
    
    // 尾set(SortedSet接口中的方法)
    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();
    }

    // 返回小于e的最大的元素
    public E lower(E e) {
        return m.lowerKey(e);
    }

    // 返回小于等于e的最大的元素
    public E floor(E e) {
        return m.floorKey(e);
    }
    
    // 返回大于等于e的最小的元素
    public E ceiling(E e) {
        return m.ceilingKey(e);
    }
    
    // 返回大于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();
    }

其他方法:

// 克隆方法
    @SuppressWarnings("unchecked")
    public Object clone() {
        TreeSet<E> clone;
        try {
            clone = (TreeSet<E>) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new InternalError(e);
        }

        clone.m = new TreeMap<>(m);
        return clone;
    }

    // 序列化写出方法
    private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException {
        // Write out any hidden stuff
        s.defaultWriteObject();

        // Write out Comparator
        s.writeObject(m.comparator());

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

        // Write out all elements in the proper order.
        for (E e : m.keySet())
            s.writeObject(e);
    }

    // 序列化写入方法
    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        // Read in any hidden stuff
        s.defaultReadObject();

        // Read in Comparator
        @SuppressWarnings("unchecked")
            Comparator<? super E> c = (Comparator<? super E>) s.readObject();

        // Create backing TreeMap
        TreeMap<E,Object> tm = new TreeMap<>(c);
        m = tm;

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

        tm.readTreeSet(size, s, PRESENT);
    }

总结:

对比:

TreeSet和HashSet
相同点:
都是唯一不重复的Set集合。
不同点:
底层来说,HashSet是用Hash表来存储数据,而TreeSet是用二叉平衡树来存储数据。 功能上来说,由于TreeSet是有序的Set,可以使用SortedSet。接口的first()、last()等方法。但由于要排序,势必要影响速度。所以,如果不需要顺序的话,还是使用HashSet吧,使用Hash表存储数据的HashSet在速度上更胜一筹。如果需要顺序则TreeSet更为明智。
底层来说,HashSet是用Hash表来存储数据,而TreeSet是用二叉平衡树来存储数据。
TreeSet和TreeMap
相同点:
TreeMap和TreeSet都是有序的集合。
TreeMap和TreeSet都是非同步集合,因此他们不能在多线程之间共享,不过可以使用方法Collections.synchroinzedMap()来实现同步。
运行速度都要比Hash集合慢,他们内部对元素的操作时间复杂度为O(logN),而HashMap/HashSet则为O(1)。
不同点:
最主要的区别就是TreeSet和TreeMap非别实现Set和Map接口
TreeSet只存储一个对象,而TreeMap存储两个对象Key和Value(仅仅key对象有序)
TreeSet中不能有重复对象,而TreeMap中可以存在。

总结:

HashSet
(1)HashSet内部使用HashMap的key存储元素,以此来保证元素不重复;
(2)HashSet是无序的,因为HashMap的key是无序的;
(3)HashSet中允许有一个null元素,因为HashMap允许key为null
(4)HashSet是非线程安全的;
(5)HashSet是没有get()方法的;
TreeSet
(1)TreeSet底层使用NavigableMap存储元素;
(2)TreeSet是有序的;
(3)TreeSet是非线程安全的;
(4)TreeSet实现了NavigableSet接口,而NavigableSet继承自SortedSet接口;
(5)TreeSet实现了SortedSet接口。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值