Java 集合系列之三:Set基本操做

1. Java Set

1. Java Set 重要观点

  • Java Set接口是Java Collections Framework的成员。
  • Set不容许出现重复元素-----------无重复
  • Set不保证集合中元素的顺序---------无序
  • Set容许包含值为null的元素,但最多只能有一个null元素。
  • Set支持泛型(类型的参数化),咱们应尽量使用它。将Generics与List一块儿使用将在运行时避免ClassCastException。
  • 先去看Map,Set的实现类都是基于Map来实现的(如,HashSet是经过HashMap实现的,TreeSet是经过TreeMap实现的,LinkedHashSet是经过LinkedHashMap来实现的)。 

2. Java Set类图

Java Set接口扩展了Collection接口。Collection接口 externs Iterable接口。java

一些最经常使用的Set实现类是HashSet,LinkedHashSet,TreeSet,SortedSet,CopyOnWriteArraySet。数组

AbstractSet提供了Set接口的骨干实现,以减小实现List的工做量。安全

 

3. Java Set 方法

 boolean    add(E e) //若是 set 中还没有存在指定的元素,则添加此元素(可选操做)。
 boolean    addAll(Collection<? extends E> c) //若是 set 中没有指定 collection 中的全部元素,则将其添加到此 set 中(可选操做)。
 void      clear() //移除此 set 中的全部元素(可选操做)。
 boolean    contains(Object o) //若是 set 包含指定的元素,则返回 true。
 boolean    containsAll(Collection<?> c) //若是此 set 包含指定 collection 的全部元素,则返回 true。
 boolean    equals(Object o) //比较指定对象与此 set 的相等性。
 int       hashCode() //返回 set 的哈希码值。
 boolean    isEmpty() //若是 set 不包含元素,则返回 true。
 Iterator<E>    iterator() //返回在此 set 中的元素上进行迭代的迭代器。
 boolean    remove(Object o) //若是 set 中存在指定的元素,则将其移除(可选操做)。
 boolean    removeAll(Collection<?> c) //移除 set 中那些包含在指定 collection 中的元素(可选操做)。
 boolean    retainAll(Collection<?> c) //仅保留 set 中那些包含在指定 collection 中的元素(可选操做)。
 int       size() //返回 set 中的元素数(其容量)。
 Object[]   toArray() //返回一个包含 set 中全部元素的数组。
<T> T[]    toArray(T[] a) //返回一个包含此 set 中全部元素的数组;返回数组的运行时类型是指定数组的类型。

2. HashSet

1. HashSet 结构图

HashSet,由哈希表(其实是一个 HashMap 实例)支持。它不保证 set 的迭代顺序;特别是它不保证该顺序恒久不变。此类容许使用 null元素。!
HashSet继承了AbstractSet,实现了Cloneable和Serializable接口!并发

  • 实现了Cloneable接口,即覆盖了函数clone(),实现浅拷贝。
  • 实现了Serializable接口,支持序列化,可以经过序列化传输。

2. HashSet 重要特色

  1. 依赖于哈希表(其实是一个 HashMap 实例)(哈希表+链表+红黑树),不能够存储相同元素(排重)
  2. 底层实现是一个HashMap(保存数据),实现Set接口。(HashSet中含有一个”HashMap类型的成员变量”map,HashSet的操做函数,实际上都是经过map实现的。)
  3. 非同步,线程不安全,存取速度快(同步封装Set s = Collections.synchronizedSet(new HashSet(...));
  4. 默认初始容量为16。
  5. 加载因子为0.75:即当 元素个数 超过 容量长度的0.75倍 时,进行扩容
  6. 扩容增量:原容量的 1 倍,如 HashSet的容量为16,一次扩容后是容量为32
  7. 重写hashCode():HashSet集合排重时,须要判断两个对象是否相同,对象相同的判断能够经过hashCode值判断,因此须要重写hashCode()方法
  8. 重写equals():equals()方法是Object类中的方法,表示比较两个对象是否相等,若不重写至关于比较对象的地址, 因此咱们能够尝试重写equals方法,检查是否排重。
  9. 会根据hashcode和equals来庞端是不是同一个对象,若是hashcode同样,而且equals返回true,则是同一个对象,不能重复存放。 
  10. fail-fast机制:HashSet经过iterator()返回的迭代器是fail-fast的。
  11. 两种遍历方法:Iterator【iterator.next()】,forEach【set.toArray();】
Set<String> set = new HashSet<String>();
set.add("first");
set.add("second");
set.add("three");

// foreach
for (String string : set) {
    System.out.println(string);
}

// iterator
Iterator<String> setIterator = set.iterator();
while (setIterator.hasNext()) {
    String string = (String) setIterator.next();
    System.out.println(string);
}

3. TreeSet

1. TreeSet 结构图

基于 TreeMap 的 NavigableSet 实现。使用元素的天然顺序对元素进行排序,或者根据建立 set 时提供的 Comparator进行排序,具体取决于使用的构造方法。异步

  TreeSet也不能存放重复对象,可是TreeSet会自动排序,若是存放的对象不能排序则会报错,因此存放的对象必须指定排序规则。排序规则包括天然排序和客户排序。函数

  ①天然排序:TreeSet要添加哪一个对象就在哪一个对象类上面实现java.lang.Comparable接口,而且重写comparaTo()方法,返回0则表示是同一个对象,不然为不一样对象。性能

  ②客户排序:创建一个第三方类并实现java.util.Comparator接口。并重写方法。定义集合形式为TreeSet ts = new TreeSet(new 第三方类());atom


TreeSet继承了AbstractSet,实现了NavigableSet、Cloneable和Serializable接口!spa

  • 继承于AbstractSet,AbstractSet实现了equals和hashcode方法。
  • 实现了NavigableSet接口,意味着它支持一系列的导航方法。好比查找与指定目标最匹配项。
  • 实现了Cloneable接口,即覆盖了函数clone(),实现浅拷贝。
  • 实现了Serializable接口,支持序列化,可以经过序列化传输。
  •  TreeSet是SortedSet接口的实现类

2. TreeSet 重要特色

  1. 依赖于TreeMap,TreeSet是基于TreeMap实现的。(红黑树)复杂度为O(log (n))
  2. 不能够存储相同元素(排重),自动排序。(有序集合)
  3. TreeSet中不容许使用null元素!在添加的时候若是添加null,则会抛出NullPointerException异常。
  4. TreeSet是非同步的方法【SortedSet s = Collections.synchronizedSortedSet(new TreeSet(...));】。
  5. 它的iterator 方法返回的迭代器是fail-fast的。
  6. TreeSet不支持快速随机遍历,只能经过迭代器进行遍历! 两种遍历方法:Iterator【iterator.next()】,forEach【set.toArray();】
  7. TreeSet中的元素支持2种排序方式:天然排序 或者 根据建立TreeSet 时提供的 Comparator 进行排序。这取决于使用的构造方法。
  8. 天然排序,重写compareTo方法:元素所属的类须要实现java.lang.Comparable接口,并重写compareTo方法。 compareTo方法除了能够进行排序外,还有排重的功能,可是必须在compareTo方法中对类中全部的属性值都进行判断,不然不比较那个属性,排重就会忽略哪一个属性
  9. 定制排序,重写compare方法:元素须要经过java.util.Comparator接口(比较器)中的compare方法进行比较大小,并排序。 compare方法除了能够进行排序外,还有排重的功能,可是必须在compare方法中对类中全部的属性值都进行判断,不然不比较那个属性,排重就会忽略哪一个属性

  ps:Comparable中的compareTo()一个参数, Comparator中compare()两个参数,返回值都是int类型,若是返回0,表示两个比较元素相同,若是大于0 ,前面大于后面,若是小于0,前面小于后面。线程

3. HashSet vs TreeSet

  1. HashSet是一个无序的集合,基于HashMap实现;TreeSet是一个有序的集合,基于TreeMap实现。
  2. HashSet集合中容许有null元素,TreeSet集合中不容许有null元素。
  3. HashSet和TreeSet都是非同步!在使用Iterator进行迭代的时候要注意fail-fast。

4. LinkedHashSet

1. LinkedHashSet 结构图

LinkedHashSet类:LinkedHashSet正好介于HashSet和TreeSet之间,它也是一个hash表,但它同时维护了一个双链表来记录插入的顺序,基本方法的复杂度为O(1)。

当遍历该集合时候,LinkedHashSet将会以元素的添加顺序访问集合的元素。

2. LinkedHashSet 重要特色

  1. 继承自HashSet,与HashSet惟一的区别是LinkedHashSet内部使用的是LinkHashMap((哈希表+链表+红黑树)+双向链表)。
  2.  LinkedHashSet在迭代访问Set中的所有元素时,性能比HashSet好,可是插入时性能稍微逊色于HashSet。
  3. 非同步,线程不安全,存取速度快(同步封装 Set s = Collections.synchronizedSet(new LinkedHashSet(...));
  4. 其余同HashSet
  5. 维护插入顺序,LinkedHashSet使用LinkedHashMap对象来存储它的元素,插入到LinkedHashSet中的元素其实是被看成LinkedHashMap的键保存起来的
  6. LinkedHashMap的每个键值对都是经过内部的静态类Entry<K, V>实例化的。这个 Entry<K, V>类继承了HashMap.Entry类。这个静态类增长了两个成员变量,before和after来维护LinkedHasMap元素的插入顺序。这两个成员变量分别指向前一个和后一个元素,这让LinkedHashMap也有相似双向链表的表现。

 

4. ConcurrentSkipListSet

1. ConcurrentSkipListSet 结构图

2. ConcurrentSkipListSet 重要特色

  1. 一个基于 ConcurrentSkipListMap 的可缩放并发 NavigableSet 实现。set 的元素能够根据它们的天然顺序进行排序,也能够根据建立 set 时所提供的 Comparator 进行排序,具体取决于使用的构造方法。
  2. 此实现为 containsaddremove 操做及其变体提供预期平均 log(n) 时间开销。多个线程能够安全地并发执行插入、移除和访问操做。迭代器是弱一致 的,返回的元素将反映迭代器建立时或建立后某一时刻的 set 状态。它们 抛出 ConcurrentModificationException,能够并发处理其余操做。升序排序视图及其迭代器比降序排序视图及其迭代器更快。
  3. 请注意,与在大多数 collection 中不一样,这里的 size 方法不是 一个固定时间 (constant-time) 操做。因为这些 set 的异步特性,肯定元素的当前数目须要遍历元素。此外,批量操做 addAllremoveAllretainAll 和 containsAll 并不 保证能以原子方式 (atomically) 执行。例如,与 addAll 操做一块儿并发操做的迭代器只能查看某些附加元素。
  4. 此类不容许使用 null 元素,由于没法可靠地将 null 参数及返回值与不存在的元素区分开来。

4. CopyOnWriteArraySet (JUC)

1. CopyOnWriteArraySet 结构图

2. CopyOnWriteArraySet 重要特色

  1. CopyOnWriteArraySet 是线程安全的 Set,它是 由 CopyOnWriteArrayList 实现内部持有一个 CopyOnWriteArrayList 引用,全部的操做都是由 CopyOnWriteArrayList 来实现的,区别就是 CopyOnWriteArraySet 是无序的,而且不容许存放重复值。因为是一个Set,因此也不支持随机索引元素。
  2. 适合元素比较少,而且读取操做高于更新(add/set/remove)操做的场景
  3. 因为每次更新须要复制内部数组,因此更新操做(addset 和 remove 等等)开销比较大。
  4. 内部的迭代器 iterator 使用了不变的“快照”技术,存储了内部数组快照, 因此它的 iterator 不支持可变remove、set、add操做,可是经过迭代器进行并发读取时效率很高。
  5. 它是线程安全的。 
相关文章
相关标签/搜索