Java之集合(十五)Set综述

  转载请注明源出处:http://www.cnblogs.com/lighten/p/7427554.html

1.前言

  原本按照顺序应该是list、queue然后就是set的讲解,但是因为set的实现比较容易,但是其容易是建立在map的实现之上才容易的,所以先讲解的map。现在对于set集合,就一次全部介绍完。Set集合特点就是没有重复的内容,其实现是通过map的键来实现的。

2.HashSet

  HashSet的结构就是持有一个HashMap,map的键就是Set的内容,值全部是PRESENT这个对象。

  构造方法都是与HashMap基本一致。

    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();
    }

  其它的方法实在没什么可说明的,都是map的相关方法,看看就可以了。

3.LinkedHashSet

  LinkedHashSet继承自HashSet,其构造方法调用的都是父类HashSet的构造方法:

  可以看出其入参dummy是没有什么用的,只是用来区分调用的是父类的哪个构造参数而已,值是什么无所谓,最终使用的是LinkedHashMap。其它的方法都是父类的方法。也没有什么可说的。顺便一提,LinkedHashMap的特性是其值是有序的,所以LinkedHashSet的迭代器也是有序的,默认是insert顺序,而不是access顺序,具体见LinkedHashMap那章介绍。

4.TreeSet

  LinkedHashSet只能按照先后顺序来进行排序,TreeSet则是按照比较器给的比较规则进行从小到大排序。其实现也就是借助于TreeMap。

  和HashSet的过程基本一致,实现也都是借助TreeMap的相关方法,所以也没有必要介绍了。

5.EnumSet

  EnumSet的实现有些不一样,其并没有使用EnumMap,而是自己实现。这个类还是一个抽象类,里面的方法全是静态方法。

  数据结构就一个枚举类的Class对象,和该枚举类的枚举实例。其构造方法是default域,我们没有办法进行直接创建。不过其静态方法提供的创建对象的能力。主要分为两个方法:noneOf和allOf。

  noneOf的含义有些让人误解,因为枚举实例都放入了EnumSet中啊,为什么还称作是空?实际上仔细看就会明白,这个构造方法中并没有size等方法,所以虽然数组中有内容,但是却返回的大小都是0。allOf调用了抽象方法addAll(),也仅仅是改变了数量而已。先注意,枚举类的实例个数是影响了具体的类。<=64个的时候,使用的是RegularEnumSet,大于的时候是JumboEnumSet。

  初看RegularEnumSet的源码会一脸懵逼,不是说是addAll是计数吗?为什么是位运算?实际上我们要明确一点,枚举类的删除添加修改等操作并不是真正的这么做了,而是打了一个标记而已。这也就解释了RegularEnumSet为什么必须枚举长度小于等于64,因为long类型只能表示64位。超过64位long不能再表示,所以使用另一个类JumboEnumSet来处理,其有个与实例对应的标志位数组(这个数组也不是一般的数组,本质上还是充分利用了long的位数,并没有一个数组位表示一个实例)和计数字段size来处理。

  其它的添加,删除,获取的逻辑都是基于其标志位的内容来处理的,实际上枚举类的实例数组并没有变化。处理逻辑就不再描述,按照位运算一个个去尝试就可以了。RegularEnumSet的long标志表示是从右边开始算第0位,JumboEnumSet的标志位数组long[]的结构是,前64个元素标志位用long数组的第一个表示,和RegularEnumSet实现一样,从右到左计数,再64个用数组中第2个表示,也是从右到左。

  RegularEnumSet:00000000 00000000 00000000 00001111

  对应枚举实例下标:63                 3210

  JumboEnumSet:long[] 数组长度为2

                  long[0]

            11111111 11111111 11111111 11111111

  对应枚举实例下标:   63                               0

                  long[1]

            00000000 00000000 00000000 00001111

  对应枚举实例下标:  127                              64

posted @ 2017-08-25 13:24  dark_saber  阅读(347)  评论(0编辑  收藏  举报