【Java】---Collection体系集合:Set、HashSet、TreeSet详解

java之集合—Set、HashSet、TreeSet详解



一、Set

1.1 概念

Set是Collection接口的子接口,是集合的规范之一。
特点:无序、无下标、元素不可重复。
Set的实现类有:HashSetLinkedHashSetTreeSet
由于Set是接口,不能直接实现,只能通过向上转型的方式创建对象,以HashSet为例:

Set set = new HashSet();//通过向上转型的方式创建对象

1.2 方法

Set继承了Collection接口,其拥有的方法继承自Collection接口,此处不再赘述,如想了解请移步:java之集合–Collection父接口及其方法


二、HashSet

2.1 概念

HashSet是Set接口的实现类之一,其存储结构为哈希表。
特点:存储结构为哈希表,通过计算元素的哈希值(hashCode)来为其分配存储空间。

由于HashSet是具体类,可以直接创建对象:

HashSet set = new HashSet();

2.2 哈希表

哈希表是一种存储结构,在java中,其结构可大致看为:数组+链表。
以下面的元素为例:

[a,b,c,d,e,f]

我们的任务是通过哈希表将上述序列存起来。
a是第一个元素,先获取a的哈希值,之后通过计算为其分配位置。(哈希值的计算我们下次再讲,主要领会流程)
哈希表1
a存储之后,下一个是b,先获取b的哈希值,之后通过计算为其分配位置。
哈希表2
b存储之后,下一个是c,先获取c的哈希值,发现计算后c的位置与a的一样,开始进行判断。
a.equals©:判断元素是否相同
若相同,拒绝此元素加入
若不同,元素c作为a的指向,与a形成链表结构:
哈希表3
c存储之后,下一个是d,先获取d的哈希值,之后通过计算为其分配位置。
哈希表4
d存储之后,下一个是e,先获取e的哈希值,发现计算后e的位置与a的一样,开始进行判断。
a.equals(e):判断元素是否相同
若相同,拒绝此元素加入
若不同,元素c作为a的指向,但是c已经是a的指向了,继续判断:
c.equals(e):判断元素是否相同
若相同,拒绝此元素加入
若不同,元素e作为c的指向,与c形成链表结构:
哈希表5
e存储之后,下一个是f,先获取f的哈希值,发现计算后e的位置与b的一样,开始进行判断。
b.equals(f):判断元素是否相同
若相同,拒绝此元素加入
若不同,元素f作为b的指向,与b形成链表关系:
哈希表6
至此,所有元素都存入了哈希表中。

数组的优点:查找快。
缺点:增删慢,长度固定

链表的优点:增删快。
缺点:查找慢

哈希表这种数组+链表的结构,融合了两者的优点,同时也弥补了各自的不足。、


三、TreeSet

3.1 概念

TreeSet是Set接口的实现类之一,其存储结构为红黑树(平衡二叉查找树)。

3.2 二叉查找树

了解红黑树之前,我们先了解一下二叉查找树。
特点:比根节点小的数放在根节点左边,比根节点大的数放在根节点右边。
以下面的序列为例:

[10,5,18,3,21,19,4,59,33,9]

上述序列没有重复元素,我们的任务是通过二叉查找树的特点将其存起来。
存入根节点10
10作为序列的起始点,将其作为根节点表示。
接下来是5,5小于10,所以存在10的左边。
存入5
5之后是18,18大于10,所以存在10的右边。
注意:每次存数时,都是从根节点(10)开始检查!
存入18
18之后是3,3小于10,所以存在10左边。
10的左边有元素5了,继续判断,3小于5,所以存在5的左边。
存入3
3之后是21,21大于10,所以存在10的右边。
10的右边有元素18了,继续判断,21大于18,所以存在18的右边。
存入21
21之后是19,19大于10,所以存在10的右边。
10的右边有元素18了,继续判断,19大于18,所以存在18的右边。
18的右边有元素21了,继续判断,19小于21,所以存在21的左边。
存入19
19之后是4,4小于10,所以存在10的左边。
10的左边有元素5了,继续判断,4小于5,所以存在5的左边。
5的左边有元素3了,继续判断,4大于3,所以存在3的右边。
存入4
4之后是59,59大于10,所以存在10的右边。
10的右边有元素18了,继续判断,59大于18,所以存在18的右边。
18的右边有元素21了,继续判断,59大于21,所以存在21的右边。
存入59
59之后是33,33大于10,所以存在10的右边。
10的右边有元素18了,继续判断,33大于18,所以存在18的右边。
18的右边有元素21了,继续判断,33大于21,所以存在21的右边。
21的右边有元素59了,继续判断,33小于59,所以存在59的左边。
存入33
33之后是9,9小于10,所以存在10的左边。
10的左边有元素5了,继续判断,9大于5,所以存在5的右边。
存入9
9之后没有元素了,序列存储完毕。
以上的图片演示,是一棵二叉查找树建立的完整过程。

为什么会存在这种看起来很麻烦的存储方式呢?

树,是一种数据结构,二叉查找树是树的其中一种表现形式。

树存在三种遍历方式:前序遍历中序遍历后序遍历

其中的前、中、后指的都是根节点的位置。

前序遍历:根,左,右
中序遍历:左,根,右
后序遍历:左,右,根

由于篇幅限制,想要了解中序遍历的朋友,请移步:【数据结构】树—中序遍历

二叉查找树经过中序遍历后,遍历出来的数据是排好序的。将上图中的树经过中序遍历后,得到的序列为:

[3,4,5,9,10,18,19,21,33,59]

对比之前毫无顺序的数据,还是排序的更一目了然。
但这并不是红黑树,红黑树是平衡二叉查找树,不过了解二叉查找树是了解平衡二叉查找树的基础,在以后我会讲解。

3.3 Comparable 和 Comparator

很多新手在使用TreeSet存储数据时,往往编译时会报错:

Student stu = new Student("张三","男",18);
TreeSet set = new TreeSet();
set.add(stu);
System.out.println(set);

错误:
编译错误
这是因为TreeSet是红黑树的存储结构,需要指定排序。针对这种问题,有两种解决方式:
1.令元素的类实现接口Comparable,并重写CompareTo方法:

public class Student implements Comparable{
    String name;
    String gender;
    int age;
    @Override
    public int compareTo(Object o) {//重写方法
        //1代表顺序颠倒;0代表是相同元素,不载入;-1代表不更改顺序。
        return 0;
    }
    public Student(String name, String gender, int age) {
        this.name = name;
        this.gender = gender;
        this.age = age;
    }
}

2.创建实现比较器:Comparator接口的类,并重写compare方法。
此处以静态匿名类示范:

Comparator cmp = new Comparator(){
            @Override
            public int compare(Object o1, Object o2) {
                Student stu1 = (Student) o1;
                Student stu2 = (Student) o2;
                if(stu1.age > stu1.age)
                    return 1;//1为颠倒顺序
                return 0;
            }
        };

然后将cmp作为TreeSet的参数:

TreeSet set = new TreeSet(cmp);

之后就可以正常传入数据了。


总结

Set子接口有两大实现类:HashSetTreeSet
HashSet以哈希表作为存储结构,TreeSet以红黑树作为存储结构。
若想要以TreeSet类存储元素,存储的元素的类型需要实现Comparable接口,或者在定义时以Compare类作为参数。

新人一个,撰写文章时可能会有一些漏缺点和不足点,希望大家在评论区帮忙批评改正,各位多多海涵。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值