关键词搜索

源码搜索 ×
×

Java集合框架:Set(HashSet,LinkedHashSet,TreeSet)

发布2016-03-17浏览4571次

详情内容


欢迎支持笔者新作:《深入理解Kafka:核心设计与实践原理》和《RabbitMQ实战指南》,同时欢迎关注笔者的微信公众号:朱小厮的博客。


欢迎跳转到本文的原文链接:https://honeypps.com/java/java-collection-set/

Set概述

 Set几乎都是内部用一个Map来实现, 因为Map里的KeySet就是一个Set,而value是假值,全部使用同一个Object。Set的特征也继承了那些内部Map实现的特征。


HashSet

1. 定义

package java.util;
public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable
{
    private transient HashMap<E,Object> map;
    private static final Object PRESENT = new Object();
    public HashSet() {
        map = new HashMap<>();
    }
//其余省略
}

    2. 概述

     HashSet是基于HashMap来实现的,操作很简单,更像是对HashMap做了一次“封装”,而且只使用了HashMap的key来实现各种特性,而HashMap的value始终都是PRESENT。
    HashSet不允许重复(HashMap的key不允许重复,如果出现重复就覆盖),允许null值,非线程安全
     罗列几个主要方法:

    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();
        }
    
      13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    3. 使用

     案例1:

    		Set<String> set = new HashSet<>();
            set.add("s2");
            set.add("s3");
            set.add("s4");
            set.add("s2");
            set.add("s5");
            set.add("s1");
            set.add(null);
            set.add("s21");
            set.add("sw2");
            set.add("s2");
            for(String i:set)
                System.out.println(i);
            System.out.println(set);
    
      13
    • 14

     输出:

    null
    s2
    s1
    sw2
    s21
    s5
    s3
    s4
    [null, s2, s1, sw2, s21, s5, s3, s4]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    LinkedHashSet

    1. 定义(整个LinkedHashSet的代码)

    package java.util;
    public class LinkedHashSet<E>
        extends HashSet<E>
        implements Set<E>, Cloneable, java.io.Serializable {
        private static final long serialVersionUID = -2851667679971038690L;
    
        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);
        }
    }
    
      13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    2. 概述

     HashSet有4个共有的构造函数:

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

     还有一个包级私有的构造函数:

        HashSet(int initialCapacity, float loadFactor, boolean dummy) {
            map = new LinkedHashMap<>(initialCapacity, loadFactor);
        }
    
    • 1
    • 2
    • 3

     这个构造函数就是LinkedHashSet的关键,整个LinkedHashSet的四个构造函数全部都是调用了HashSet中的这个包级私有的构造函数(构造函数中的dummy就是标记,无实用,为了实现方法的重载而已),也就是说LinkedHashSet就是基于LinkedHashMap实现的

    3. 使用

    案例2:

            Set<String> set = new LinkedHashSet<>();
            set.add("s2");
            set.add("s3");
            set.add("s4");
            set.add("s2");
            set.add("s5");
            set.add("s1");
            set.add(null);
            set.add("s21");
            set.add("sw2");
            set.add("s2");
            for(String i:set)
                System.out.println(i);
            System.out.println(set);
    
      13
    • 14

     输出:

    s2
    s3
    s4
    s5
    s1
    null
    s21
    sw2
    [s2, s3, s4, s5, s1, null, s21, sw2]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    允许null值,保留插入顺序,非线程安全。


    TreeSet

    1. 定义

    package java.util;
    public class TreeSet<E> extends AbstractSet<E>
        implements NavigableSet<E>, Cloneable, java.io.Serializable
    {
        private transient NavigableMap<E,Object> m;
        private static final Object PRESENT = new Object();
        public TreeSet() {
            this(new TreeMap<E,Object>());
        }
        public TreeSet(Comparator<? super E> comparator) {
            this(new TreeMap<>(comparator));
        }
        public TreeSet(Collection<? extends E> c) {
            this();
            addAll(c);
        }
        public TreeSet(SortedSet<E> s) {
            this(s.comparator());
            addAll(s);
        }
    //其余省略
    }
    
      13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    2. 概述

     TreeSet的内部基于TreeMap实现,同样value永远为PRESENT.
     案例3:

            Set<String> set = new TreeSet<String>();
            set.add("s2");
            set.add("s3");
            set.add("s4");
            set.add("s2");
            set.add("s5");
            set.add("s1");
    //        set.add(null);
            set.add("s21");
            set.add("sw2");
            set.add("s2");
            for(String i:set)
                System.out.println(i);
            System.out.println(set);
    
      13
    • 14

     运行结果:

    s1
    s2
    s21
    s3
    s4
    s5
    sw2
    [s1, s2, s21, s3, s4, s5, sw2]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    不允许重复,不允许null值(如果有基于null的比较器,就可以允许为null),默认按升序排列。
    案例4(其中的Person2详细见《Comparable与Comparator浅析》):

            Set<Person2> set = new TreeSet<Person2>(new Comparator<Person2>(){
                @Override
                public int compare(Person2 o1, Person2 o2)
                {
                    if(o1==null || o2==null)
                        return 0;
                    return o1.getAge()-o2.getAge();
                }
            });
            Person2 p1 = new Person2("zzh",18);
            Person2 p2 = new Person2("jj",17);
            Person2 p3 = new Person2("qq",19);
            Person2 p4 = new Person2(null,19);
            set.add(p1);
            set.add(p2);
            set.add(p3);
            set.add(p4);
    
            System.out.println(set);
    
      13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

     输出结果:[jj:17, zzh:18, qq:19]
     可以看到不是强制性要求TreeSet的键为null。


    参考资料:

    1. Comparable与Comparator浅析

    欢迎跳转到本文的原文链接:https://honeypps.com/java/java-collection-set/

    欢迎支持笔者新作:《深入理解Kafka:核心设计与实践原理》和《RabbitMQ实战指南》,同时欢迎关注笔者的微信公众号:朱小厮的博客。


    相关技术文章

    点击QQ咨询
    开通会员
    返回顶部
    ×
    微信扫码支付
    微信扫码支付
    确定支付下载
    请使用微信描二维码支付
    ×

    提示信息

    ×

    选择支付方式

    • 微信支付
    • 支付宝付款
    确定支付下载