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接口。
一些最常用的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 重要特点
- 依赖于哈希表(实际上是一个 HashMap 实例)(哈希表+链表+红黑树),不可以存储相同元素(排重)
- 底层实现是一个HashMap(保存数据),实现Set接口。(HashSet中含有一个”HashMap类型的成员变量”map,HashSet的操作函数,实际上都是通过map实现的。)
- 非同步,线程不安全,存取速度快(同步封装Set s = Collections.synchronizedSet(new HashSet(...));)
- 默认初始容量为16。
- 加载因子为0.75:即当 元素个数 超过 容量长度的0.75倍 时,进行扩容
- 扩容增量:原容量的 1 倍,如 HashSet的容量为16,一次扩容后是容量为32
- 重写hashCode():HashSet集合排重时,需要判断两个对象是否相同,对象相同的判断可以通过hashCode值判断,所以需要重写hashCode()方法
- 重写equals():equals()方法是Object类中的方法,表示比较两个对象是否相等,若不重写相当于比较对象的地址, 所以我们可以尝试重写equals方法,检查是否排重。
- 会根据hashcode和equals来庞端是否是同一个对象,如果hashcode一样,并且equals返回true,则是同一个对象,不能重复存放。
- fail-fast机制:HashSet通过iterator()返回的迭代器是fail-fast的。
- 两种遍历方法: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 第三方类());
TreeSet继承了AbstractSet,实现了NavigableSet、Cloneable和Serializable接口!
- 继承于AbstractSet,AbstractSet实现了equals和hashcode方法。
- 实现了NavigableSet接口,意味着它支持一系列的导航方法。比如查找与指定目标最匹配项。
- 实现了Cloneable接口,即覆盖了函数clone(),实现浅拷贝。
- 实现了Serializable接口,支持序列化,能够通过序列化传输。
- TreeSet是SortedSet接口的实现类
2. TreeSet 重要特点
- 依赖于TreeMap,TreeSet是基于TreeMap实现的。(红黑树)复杂度为O(log (n))
- 不可以存储相同元素(排重),自动排序。(有序集合)
- TreeSet中不允许使用null元素!在添加的时候如果添加null,则会抛出NullPointerException异常。
- TreeSet是非同步的方法【SortedSet s = Collections.synchronizedSortedSet(new TreeSet(...));】。
- 它的iterator 方法返回的迭代器是fail-fast的。
- TreeSet不支持快速随机遍历,只能通过迭代器进行遍历! 两种遍历方法:Iterator【iterator.next()】,forEach【set.toArray();】
- TreeSet中的元素支持2种排序方式:自然排序 或者 根据创建TreeSet 时提供的 Comparator 进行排序。这取决于使用的构造方法。
- 自然排序,重写compareTo方法:元素所属的类需要实现java.lang.Comparable接口,并重写compareTo方法。 compareTo方法除了可以进行排序外,还有排重的功能,但是必须在compareTo方法中对类中所有的属性值都进行判断,否则不比较那个属性,排重就会忽略哪个属性
- 定制排序,重写compare方法:元素需要通过java.util.Comparator接口(比较器)中的compare方法进行比较大小,并排序。 compare方法除了可以进行排序外,还有排重的功能,但是必须在compare方法中对类中所有的属性值都进行判断,否则不比较那个属性,排重就会忽略哪个属性
ps:Comparable中的compareTo()一个参数, Comparator中compare()两个参数,返回值都是int类型,如果返回0,表示两个比较元素相同,如果大于0 ,前面大于后面,如果小于0,前面小于后面。
3. HashSet vs TreeSet
- HashSet是一个无序的集合,基于HashMap实现;TreeSet是一个有序的集合,基于TreeMap实现。
- HashSet集合中允许有null元素,TreeSet集合中不允许有null元素。
- HashSet和TreeSet都是非同步!在使用Iterator进行迭代的时候要注意fail-fast。
4. LinkedHashSet
1. LinkedHashSet 结构图
LinkedHashSet类:LinkedHashSet正好介于HashSet和TreeSet之间,它也是一个hash表,但它同时维护了一个双链表来记录插入的顺序,基本方法的复杂度为O(1)。
当遍历该集合时候,LinkedHashSet将会以元素的添加顺序访问集合的元素。
2. LinkedHashSet 重要特点
- 继承自HashSet,与HashSet唯一的区别是LinkedHashSet内部使用的是LinkHashMap((哈希表+链表+红黑树)+双向链表)。
- LinkedHashSet在迭代访问Set中的全部元素时,性能比HashSet好,但是插入时性能稍微逊色于HashSet。
- 非同步,线程不安全,存取速度快(同步封装 Set s = Collections.synchronizedSet(new LinkedHashSet(...));)
- 其他同HashSet
- 维护插入顺序,LinkedHashSet使用LinkedHashMap对象来存储它的元素,插入到LinkedHashSet中的元素实际上是被当作LinkedHashMap的键保存起来的
- LinkedHashMap的每一个键值对都是通过内部的静态类Entry<K, V>实例化的。这个 Entry<K, V>类继承了HashMap.Entry类。这个静态类增加了两个成员变量,before和after来维护LinkedHasMap元素的插入顺序。这两个成员变量分别指向前一个和后一个元素,这让LinkedHashMap也有类似双向链表的表现。
4. ConcurrentSkipListSet
1. ConcurrentSkipListSet 结构图
2. ConcurrentSkipListSet 重要特点
- 一个基于
ConcurrentSkipListMap
的可缩放并发NavigableSet
实现。set 的元素可以根据它们的自然顺序进行排序,也可以根据创建 set 时所提供的Comparator
进行排序,具体取决于使用的构造方法。 - 此实现为 contains、add、remove 操作及其变体提供预期平均 log(n) 时间开销。多个线程可以安全地并发执行插入、移除和访问操作。迭代器是弱一致 的,返回的元素将反映迭代器创建时或创建后某一时刻的 set 状态。它们不 抛出
ConcurrentModificationException
,可以并发处理其他操作。升序排序视图及其迭代器比降序排序视图及其迭代器更快。 - 请注意,与在大多数 collection 中不同,这里的 size 方法不是 一个固定时间 (constant-time) 操作。由于这些 set 的异步特性,确定元素的当前数目需要遍历元素。此外,批量操作 addAll、removeAll、retainAll 和 containsAll 并不 保证能以原子方式 (atomically) 执行。例如,与 addAll 操作一起并发操作的迭代器只能查看某些附加元素。
- 此类不允许使用 null 元素,因为无法可靠地将 null 参数及返回值与不存在的元素区分开来。
4. CopyOnWriteArraySet (JUC)
1. CopyOnWriteArraySet 结构图
2. CopyOnWriteArraySet 重要特点
- CopyOnWriteArraySet 是线程安全的 Set,它是由 CopyOnWriteArrayList 实现,内部持有一个 CopyOnWriteArrayList 引用,所有的操作都是由 CopyOnWriteArrayList 来实现的,区别就是 CopyOnWriteArraySet 是无序的,并且不允许存放重复值。由于是一个Set,所以也不支持随机索引元素。
- 适合元素比较少,并且读取操作高于更新(add/set/remove)操作的场景
- 由于每次更新需要复制内部数组,所以更新操作(add、set 和 remove 等等)开销比较大。
- 内部的迭代器 iterator 使用了不变的“快照”技术,存储了内部数组快照, 所以它的 iterator 不支持可变remove、set、add操作,但是通过迭代器进行并发读取时效率很高。
- 它是线程安全的。
Java 集合系列之三:Set基本操作的更多相关文章
- java集合系列之三(ArrayList)
上一章,我们学习了Collection的架构.这一章开始,我们对Collection的具体实现类进行讲解:首先,讲解List,而List中ArrayList又最为常用.因此,本章我们讲解ArrayLi ...
- Java 集合系列之二:List基本操作
1. Java List 1. Java List重要观点 Java List接口是Java Collections Framework的成员. List允许您添加重复元素. List允许您拥有'nu ...
- Java 集合系列之五:Map基本操作
1. Java Map 1. Java Map 重要观点 Java Map接口是Java Collections Framework的成员.但是它不是Collection 将键映射到值的对象.一个映射 ...
- Java 集合系列14之 Map总结(HashMap, Hashtable, TreeMap, WeakHashMap等使用场景)
概要 学完了Map的全部内容,我们再回头开开Map的框架图. 本章内容包括:第1部分 Map概括第2部分 HashMap和Hashtable异同第3部分 HashMap和WeakHashMap异同 转 ...
- Java 集合系列 17 TreeSet
java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...
- Java 集合系列 12 TreeMap
java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...
- Java 集合系列 02 Collection架构
java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...
- Java 集合系列 01 总体框架
java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...
- 深入java集合系列文章
搞懂java的相关集合实现原理,对技术上有很大的提高,网上有一系列文章对java中的集合做了深入的分析, 先转载记录下 深入Java集合学习系列 Java 集合系列目录(Category) HashM ...
随机推荐
- 那些前端二进制操作API
一直以来,前端的工作主要涉及的是字符串操作,而对二进制的数据接触较少.但是这种需求却一直存在着,尤其是HTML5之后,随着web应用越来越复杂,File,Blob,TypedArray这些API的出现 ...
- 深入V8引擎-默认Platform之mac篇(2)
先说结论,V8引擎在默认Platform中初始化的这个线程是用于处理类似于setTimeout的延时任务. 另外附一些图,包括继承树.关键属性归属.纯逻辑工作流程,对代码木得兴趣的看完图可以X掉了. ...
- Spring+Mybatis动态切换数据源
功能需求是公司要做一个大的运营平台: 1.运营平台有自身的数据库,维护用户.角色.菜单.部分以及权限等基本功能. 2.运营平台还需要提供其他不同服务(服务A,服务B)的后台运营,服务A.服务B的数据库 ...
- aspx页面,后端通过Attributes.Add给textbox添加事件时,传参失效问题。
测试一:------------------------------------------------------------------------------------------------ ...
- 深挖Jvm垃圾收集
垃圾收集(Garbage Collection,GC),它的任务是解决以下 3 件问题: 哪些内存需要回收? 什么时候回收? 如何回收? 其中第一个问题很好回答,在 Java 中,GC 主要发生在 J ...
- 2019 中细软java面试笔试题 (含面试题解析)
本人5年开发经验.18年年底开始跑路找工作,在互联网寒冬下成功拿到阿里巴巴.今日头条.中细软等公司offer,岗位是Java后端开发,因为发展原因最终选择去了中细软,入职一年时间了,也成为了面试官 ...
- 如何让SQL语句不执行默认排序,而是按照in语句的顺序返回结果
Oracle: ')order by instr('111,222,333,444,555,666',order_id); Mysql: ') order by instr(',111,222,333 ...
- Spring Boot 使用 JWT 进行身份和权限验证
上周写了一个 适合初学者入门 Spring Security With JWT 的 Demo,这篇文章主要是对代码中涉及到的比较重要的知识点的说明. 适合初学者入门 Spring Security W ...
- JS面向对象设计-创建对象
Object构造函数和对象字面量都可以用来创建单个对象,但是在创建多个对象时,会产生大量重复代码. 1.工厂模式 工厂模式抽象了创建具体对象的过程.由于ECMAScript无法创建类,我们用函数来封装 ...
- 用node发布一个包
手把手教你用npm发布一个包 注:本文引用于简书 http://www.jianshu.com/p/36d3e0e00157 但是内容的话,还是一样的,也就是继续之前的工作,将那个autoRout ...