Java中的集合(六)继承Collection的Set接口

一、Set接口的简介

Set接口和List接口都是继承自Collection接口,它与Collection接口中功能基本一致,并没有对Collection接口的扩展,但是它比Collection接口更严格。Java中的Set接口类似于数学直观上的集合。

Set接口的特性

1、Set接口元素无序。存储和读取元素都是无序的;

2、Set接口存储的元素不能重复。

二、Set接口的类图结构

通过上面的类图可用看出:Set接口下一共有三个实现类:HashSet、LinkedHashSet和TreeSet

三、Set接口的常用方法

四、对象相等性

引用到堆上同一个对象的两个引用是相等的。如果对两个引用调用hashCode方法,会得到相同的结果,如果对象所属的类没有覆盖Object的hashCode方法的话,hashCode会返回每个对象特有的序号(java是依据对象的内存地址计算出的此序号),所以两个不同的对象的hashCode值是不可能相等的。

如果想让两个对象的引用相等,必须重写Object对象的hashCode()和equals()方法,因为Object的hashCode()方法返回的是该对象的内存地址,所以重写hashCode()方法,保证引用的两个对象具有相同的hashCode,然后通过equals()方法判断两个对象,返回true。

五、HashSet

(一)、简介及继承结构

1、简介

HashSet实现了Set接口,底层的数据结构是哈希表(HashMap实例),HashSet的元素是根据哈希表的哈希值存取的,由于实现Set接口,所以HashSet存储的元素是无序,不可重复的。

3、继承结构

通过结构图可以看出,HashSet继承自AbstractSet,实现了Set, Cloneable(JDK1.8*), java.io.Serializable这些接口。

    • 继承AbstractSet,实现了Set接口:提供了相关添加,删除,修改和遍历等功能;
    • 实现Cloneable接口:覆盖函数clone(),可以被克隆;
    • 实现Serializable接口:支持序列化和反序列化,可以通过序列化传输数据。

3、特性

    • 底层数据结构是哈希表(HashMap实例);
    • 元素无序(根据哈希表的哈希值存取);
    • 元素不可重复(通过hashCode和equals方法);
    • 线程不安全(非同步);
    • 集合元素可以是null,且只有一个null。

(二)、HashSet的构造方法

(三)、HashSet如何保证唯一性

HashSet通过元素的hashCode()和equals()方法判断元素是否重复,从而保证元素的唯一性。

当HashSet调用add(E e)方法时,元素e调用hashCode()方法获取哈希值,根据此哈希值判断集合中是否存在相同的哈希值,

1、调用hashCode()方法,哈希值不同的情况

  如果哈希值不同,则直接添加。

2、调用hashCode()方法,哈希值相同的情况

如果哈希值相同同,则取出集合中与此哈希值相同的对象,遍历这些对象时通过元素e调用equals()方法判断是否相同,不同则添加。

因此,当添加的对象时自定义类时,必须重写hashCode()和equals()方法,来确保对象具有相同哈希值。

六、LinkedHashSet

(一)、简介及继承结构

1、简介

LinkedHashSet直接继承自HashSet,底层数据结构由哈希表和链表组成。哈希表保证元素的唯一性,链表保证元素有序(存储和取出顺序是一致的)。

3、继承结构

LinkedHashSet继承自HashSet,所以继承结构和HashSet类似,可以参考HashSet,这里不再赘述。

3、特性

    • 底层数据结构有哈希表和链表组成(哈希表保证元素的唯一性,链表保证元素有序(存储和取出顺序是一致的))。
    • 元素有序(基于链表);
    • 元素不可重复(基于哈希表);
    • 线程不安全(非同步);
    • 允许有null值。

(二),LinkedHashSet的构造方法

(三)、LinkedHashSet的排序

LinkedHashSet使用LinkedHashMap对象存储元素,添加到LinkedHashSet的元素实际上当做LinkedHashMap的键保存起来。

LinkedHashMap的每一个键值对都是通过内部的静态类Entry<K, V>实例化的。这个 Entry<K, V>类继承了HashMap.Entry类。

这个静态类增加了两个成员变量,before和after来维护LinkedHasMap元素的插入顺序。这两个成员变量分别指向前一个和后一个元素,这让LinkedHashMap也有类似双向链表的表现。

通过上图LinkedHashMap的部分源码可以看出,LinkedHashMap定义了内部类Entry的两个变量before,after,维护了插入元素的顺序。定义了两个成员变量head,tail保存头节点和尾节点。

(四)、LinkedHashSet如何保证唯一性

LinkedHashSet通过LinkedHashMap存储对象,通过hashCode()和equals()方法保证元素的唯一性,过程与HashSet类似,可参考HashSet添加元素的过程,这里不再赘述。

(五)、LinkedHashSet与HashSet的区别

1、元素顺序。HashSet元素无序,LinkedHashSet元素有序;

2、数据结构。HashSet底层数据结构是哈希表(HashMap实例),LinkedHashSet底层数据结构是哈希表和链表;

3、性能。迭代遍历时,HashSet比不上LinkedHashSet,插入元素时,HashSet比LinkedHashSet强。

七、TreeSet

(一)、简介及继承结构

1、简介

TreeSet继承自Set接口,底层数据结构是二叉树,并且是红黑树,底层依赖于TreeMap,元素是有序,不可重复的。

3、继承结构

TreeSet 是继承AbstractSet,实现了Set接口,特性和HashSet结构类似。

TreeSet 实现了NavigableSet接口,意味着它支持一系列的导航方法。比如查找与指定目标最匹配项。

3、特性

    • 元素有序;
    • 元素不可重复;
    • 线程不安全的;
    • 允许有null值;
    • TreeSet不支持快速随机遍历,只能通过迭代器进行遍历。

(二)、TreeSet的构造方法

(三)、TreeSet排序

TreeSet是基于TreeMap实现的,底层数据结构是红黑树,支持两种排序方式:自然排序和定制排序。

Java中提供了Comparable和Comparator接口都是为了对类进行比较可以把Comparable理解为内部比较器,而Comparator是外部比较器,具体概念在Java中的集合(三)继承Collection的Queue接口 已经说明,感兴趣的可以点击查看。

1、自然排序

TreeSet会调用实现了Comparable接口的集合元素的compareTo(Objec o)方法来比较元素之间的大小关系,然后将集合元素按升序排列,这就是自然排序。

    • 返回正数:往二叉树的右边添加
    • 返回负数:往二叉树的左边添加
    • 返回 0 : 说明重复,不添加

2、定制排序

定制排序,集合元素必须要实现Comparator接口的int compare(T o1,T o2)方法,定制特有的排序方式。

(四)、TreeSet如何保证唯一性

TreeSet是Set的实现类,元素不可重复的原理和HashSet类似,具体可参考HashSet。

(五)、TreeSet与HashSet的区别

1、底层数据结构。HashSet是基于哈希表的,TreeSet是基于二叉树(红黑树的);

2、元素排序。HashSet元素是无序的,TreeSet元素是有序的;

(六)、TreeSet的数据结构:红黑树

1、红黑树的简介

红黑树是特殊的二叉查找树,意味着它满足二叉查找树的特征:任意一个节点所包含的键值,大于等于左孩子的键值,小于等于右孩子的键值。

除了具备该特性之外,红黑树还包括许多额外的信息。

红黑树的每个节点上都有存储位表示节点的颜色,颜色是红(Red)或黑(Black)。

2、特性

    • (1)每个节点或者是黑色,或者是红色。
    • (2)根节点是黑色。
    • (3)每个叶子节点是黑色。 (注意:这里叶子节点,是指为空的叶子节点!)
    • (4)如果一个节点是红色的,则它的子节点必须是黑色的。
    • (5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。

关于它的特性,需要注意的是:
第一,特性(3)中的叶子节点,是只为空(NIL或null)的节点。
第二,特性(5),确保没有一条路径会比其他路径长出俩倍。因而,红黑树是相对是接近平衡的二叉树。

3、树的旋转

红黑树的基本操作是添加删除旋转。在对红黑树进行添加或删除后,会用到旋转方法。为什么呢?道理很简单,添加或删除红黑树中的节点之后,红黑树就发生了变化,可能不满足红黑树的5条性质,也就不再是一颗红黑树了,而是一颗普通的树。而通过旋转,可以使这颗树重新成为红黑树。简单点说,旋转的目的是让树保持红黑树的特性。
旋转包括两种:左旋 和 右旋。下面分别对红黑树的基本操作进行介绍。

(1)、左旋

将子根节点变成子根节点的左节点

(2)、右旋

将子根节点变成子根节点的右节点

如下图解

4、添加操作

将一个节点插入到红黑树中,需要执行哪些步骤呢?首先,将红黑树当作一颗二叉查找树,将节点插入;然后,将节点着色为红色;最后,通过"旋转和重新着色"等一系列操作来修正该树,使之重新成为一颗红黑树。详细描述如下:

①: 将红黑树当作一颗二叉查找树,将节点插入。
       红黑树本身就是一颗二叉查找树,将节点插入后,该树仍然是一颗二叉查找树。也就意味着,树的键值仍然是有序的。此外,无论是左旋还是右旋,若旋转之前这棵树是二叉查找树,旋转之后它一定还是二叉查找树。这也就意味着,任何的旋转和重新着色操作,都不会改变它仍然是一颗二叉查找树的事实。
那接下来,通过以下三步骤,使这颗树重新成为红黑树!

②:将插入的节点着色为"红色"。
       为什么着色成红色,而不是黑色呢?为什么呢?在回答之前,我们需要重新温习一下红黑树的特性:
(1) 每个节点或者是黑色,或者是红色。
(2) 根节点是黑色。
(3) 每个叶子节点是黑色。 [注意:这里叶子节点,是指为空的叶子节点!]
(4) 如果一个节点是红色的,则它的子节点必须是黑色的。
(5) 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。
      将插入的节点着色为红色,不会违背"特性(5)"!少违背一条特性,就意味着我们需要处理的情况越少。接下来,就要努力的让这棵树满足其它性质即可;满足了的话,它就又是一颗红黑树了。

③: 通过一系列的旋转或着色等操作,使之重新成为一颗红黑树。
       第二步中,将插入节点着色为"红色"之后,不会违背"特性(5)"。那它到底会违背哪些特性呢?
       对于"特性(1)",显然不会违背了。因为我们已经将它涂成红色了。
       对于"特性(2)",显然也不会违背。在第一步中,我们是将红黑树当作二叉查找树,然后执行的插入操作。而根据二叉查找数的特点,插入操作不会改变根节点。所以,根节点仍然是黑色。
       对于"特性(3)",显然不会违背了。这里的叶子节点是指的空叶子节点,插入非空节点并不会对它们造成影响。
       对于"特性(4)",是有可能违背的!
       那接下来,想办法使之"满足特性(4)",就可以将树重新构造成红黑树了。

5、删除操作

将红黑树内的某一个节点删除。需要执行的操作依次是:首先,将红黑树当作一颗二叉查找树,将该节点从二叉查找树中删除;然后,通过"旋转和重新着色"等一系列来修正该树,使之重新成为一棵红黑树。详细描述如下:

第一步:将红黑树当作一颗二叉查找树,将节点删除。
       这和"删除常规二叉查找树中删除节点的方法是一样的"。分3种情况:
① 被删除节点没有儿子,即为叶节点。那么,直接将该节点删除就OK了。
② 被删除节点只有一个儿子。那么,直接删除该节点,并用该节点的唯一子节点顶替它的位置。
③ 被删除节点有两个儿子。那么,先找出它的后继节点;然后把“它的后继节点的内容”复制给“该节点的内容”;之后,删除“它的后继节点”。在这里,后继节点相当于替身,在将后继节点的内容复制给"被删除节点"之后,再将后继节点删除。这样就巧妙的将问题转换为"删除后继节点"的情况了,下面就考虑后继节点。 在"被删除节点"有两个非空子节点的情况下,它的后继节点不可能是双子非空。既然"的后继节点"不可能双子都非空,就意味着"该节点的后继节点"要么没有儿子,要么只有一个儿子。若没有儿子,则按"情况① "进行处理;若只有一个儿子,则按"情况② "进行处理。

第二步:通过"旋转和重新着色"等一系列来修正该树,使之重新成为一棵红黑树。
        因为"第一步"中删除节点之后,可能会违背红黑树的特性。所以需要通过"旋转和重新着色"来修正该树,使之重新成为一棵红黑树。

红黑树是一种数据结构,本小节只是说明了一些概念,具体使用还需要深入了解。

八、HashSet、LinkedHashSet和TreeSet的异同点

(一)、不同点

1、HashSet存储元素是无序的,LinkedHashSet和TreeSet存储元素是有序的;

2、HashSet是基于哈希表,LinkedHashSet是基于哈希表和链表,TreeSet是基于红黑树;

3、LinkedHashSet是基于链表的排序(存取顺序一致),TreeSet可以使用自然排序,也可定制排序。

(二)、相同点

1、存储的元素是不可重复的;

2、线程不安全的;

3、遍历方式推荐使用迭代器遍历。

Java中的集合(六)继承Collection的Set接口的更多相关文章

  1. java中的集合:继承关系和简介

    1.继承关系图 2.Collection接口 Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements).一些Collect ...

  2. Java面试题:Java中的集合及其继承关系

    关于集合的体系是每个人都应该烂熟于心的,尤其是对我们经常使用的List,Map的原理更该如此.这里我们看这张图即可: 1.List.Set.Map是否继承自Collection接口? List.Set ...

  3. Java学习--java中的集合框架、Collection接口、list接口

    与数组相比:1.数组的长度固定,而集合的长度可变2.数组只能通过下表访问元素,类型固定,而有的集合可以通过任意类型查找所映射的具体对象 java集合框架:collection(list序列,queue ...

  4. 分享知识-快乐自己:Java中各种集合特点

    Java中各种集合特点: Collection[单列集合]: List(有序,可重复): ArrayList: 底层数据结构是数组,查询快,增删慢.线程不安全,效率高. Vector: 底层数据结构是 ...

  5. JAVA笔记整理(九),JAVA中的集合

    在工作中,我们经常需要将多个对象集中存放,可以使用数组,但是数组的长度一旦固定之后是不可变的,为了保存数量确定的数据,我们可以使用JAVA中的集合. 在我看来,JAVA中的集合可以看作是一个特殊的数据 ...

  6. Java中的集合(五)继承Collection的List接口

    Java中的集合(五)继承Collection的List接口 一.List接口简介 List是有序的Collection的,此接口能够精确的控制每个元素插入的位置.用户能够根据索引(元素在List接口 ...

  7. Java中的集合(三)继承Collection的Queue接口

    Java中的集合(三)继承Collection的Queue接口 一.Queue介绍 Queue接口继承自Collection接口,是Java中定义的一种队列数据结构,元素是有序的(按插入顺序排序),先 ...

  8. Java中的集合(二)单列集合顶层接口------Collection接口

    Java中的集合(二)单列集合顶层接口------Collection接口 Collection是一个高度封装的集合接口,继承自Iterable接口,它提供了所有集合要实现的默认方法.由于Iterab ...

  9. Java中的集合框架-Collection(二)

    上一篇<Java中的集合框架-Collection(一)>把Java集合框架中的Collection与List及其常用实现类的功能大致记录了一下,本篇接着记录Collection的另一个子 ...

随机推荐

  1. 线段树 区间合并 F - Sequence operation

    F - Sequence operation 题解:这个题目不是一个特别难的题目,但是呢,写了好久,首先线段树难敲,其次就是bug难找,最后这个代码都被我改的乱七八糟的了,这个有两个地方要注意一下,一 ...

  2. Day_13【IO流】扩展案例1_读取项目文件内容并去重

    分析以下需求,并用代码实现: 需求: 读取当前项目下的info1.txt 文件内容如下 : aaaaaaaaaaabbbbbbbbbbbbccdefg 要求将数据去重后写回最终效果 : fgdebca ...

  3. 今天主要做的是Remember Me(记住我)功能的实现

    功能就是让网站登录过的人只要不注销,下次打开网站之后直接进入,不用重复登录,此功能主要是session与cookie的配合运用,具体实现是这样的,在登录页面判断并完成登录,然后将所需数据写入sessi ...

  4. matlab 调用C程序进行simulink仿真

    文章目录 simulink仿真 创建C程序 编译C程序 运行结果 simulink仿真 simulink仿真中需要使用S-Function模块,可以实现调用C程序进行仿真,下面先建立一个简单的仿真: ...

  5. FPGA六位共阳极数码管动态显示

    `timescale 1ns/1ps module adc_dis( clk , rst_n , sm_seg , sm_bit ); input clk;//50HZ input rst_n; :] ...

  6. 初探Redis-基础类型Hash

    Redis存在五种基础类型:字符串(String).队列(List).哈希(Hash).集合(Set).有序集合(Sorted Set).本次列举出Hash的常用操作. Redis官网:https:/ ...

  7. 一文带你了解Spring核心接口Ordered的实现及应用

    前言 最近在看框架的时候,发现了这个接口,在此进行总结,希望能够给大家帮助,同时提升自己. order接口的大体介绍 Spring框架中有这个一个接口,名字叫Ordered,联想我们在数据库中应用的O ...

  8. c#得出两个列表的交集

    c#提供了Intersect来得到两个列表的交集,它是通过使用默认的相等比较器对值进行比较生成两个序列的交集,定义为: public static IEnumerable<TSource> ...

  9. Android Loader使用时,屏幕解锁后,重复加载

    在使用AsyncTaskLoader时,当手机解锁后,会重复加载数据,代码如下: static class CouponShopQueryLoader extends AsyncTaskLoader& ...

  10. Analysis分析器

    一.Analysis简介 场景执行过程中,loadrunner收集执行过程中的数据,存储在扩展名为.lrr的文件中,Analysis分析器打开这个文件,对文件信息进行处理,并生成图和报告. 数据分析不 ...