Set集合类(结合底层)(2/6)

前沿

Set接口

Set 接口概述

 Set接口是Collection的子接口,set接口没有提供额外的方法 ,存储无顺序的,不可重复的数据---->高中的集合

  • HashSet: 作为Set接口的主要实现类,线程不安全,可以储存null值
  • LinkedHashSet: HashSet的子类,遍历其内部数据时,可以按照添加的顺序遍历
  • TreeSet:可以按照添加的元素,进行排序。

 Set 集合不允许包含相同的元素,如果试把两个相同的元素加入同一个 Set 集合中,则添加操作失败。

 Set 判断两个对象是否相同不是使用 == 运算符,而是根据 equals() 方法

Set:

  1. 无序性:不等于随机性,存储的数据在底层数组中并非按照数组索引的顺序添加,而是通过数据的hash值来判断
  2. 不可重复性保证添加相同元素按照equals判断时不能返回true
  3. 添加数据的过程:以HashSet为例:


下面介绍Set的几个实现类

实现类1:HashSet

实现类之一:HashSet

Collection子接口之二:Set接口 Set实现类之一:HashSet

HashSet 是 Set 接口的典型实现,大多数时候使用 Set 集合时都使用这个实现类。

HashSet 按 Hash 算法来存储集合中的元素,因此具有很好的存取、查找、删除 性能。

HashSet 具有以下特点:

不能保证元素的排列顺序

HashSet 不是线程安全的

集合元素可以是 null

存储形式既包括数组又包括链表,通过Hash算法比较哈希值存放至数组,若不同的对象hash值相同,则调用equals方法,返回false,则在hash值相同的数组元素基础之上以链表的形式存储

在这里插入图片描述

HashSet 集合判断两个元素相等的标准:

两个对象通过 hashCode() 方法比较相 等,并且两个对象的 equals() 方法返回值也相等。

对于存放在Set容器中的对象,对应的类一定要重写equals()和hashCode(Object obj)方法,以实现对象相等规则

即:“相等的对象必须具有相等的散列码”。

重写hashCode注意事项

 在程序运行时,同一个对象多次调用 hashCode() 方法应该返回相同的值。
   当两个对象的 equals() 方法比较返回 true 时,这
  两个对象的 hashCode() 方法的返回值也应相等。 

 对象中用作 equals() 方法比较的 Field,都应该用来
计算 hashCode 值

重写equals注意事项

 以自定义的Customer类为例,何时需要重写equals()? 

 当一个类有自己特有的“逻辑相等”概念,当改写equals()的时候,
总是 要改写hashCode(),根据一个类的equals方法(改写后),
两个截然不 同的实例有可能在逻辑上是相等的,但是,
根据Object.hashCode()方法, 它们仅仅是两个对象。

  因此,违反了“相等的对象必须具有相等的散列码”。

  结论:复写equals方法的时候一般都需要同时复写hashCode方法。
 通 常参与计算hashCode的对象的属性也应该参与到equals()中进行计算 


向HashSet中添加元素的过程理解

当向 HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode() 方法 来得到该对象的 hashCode 值,然后根据 hashCode 值,通过某种散列函数决定出该对象 在 HashSet 底层数组中的存储位置。

(这个散列函数会与底层数组的长度相计算得到在 数组中的下标,并且这种散列函数计算还尽可能保证能均匀存储元素,越是散列分布, 该散列函数设计的越好)

如果两个元素的hashCode()值相等,会再继续调用equals方法,如果equals方法结果 为true,添加失败; 如果为false,那么会保存该元素,但是该数组的位置已经有元素了, 那么会通过链表的方式继续链接。

如果两个元素的 equals() 方法返回 true**,但它们的 hashCode() 返回值不相 等,hashSet 将会把它们存储在不同的位置,但依然可以添加成功**。






实现类2:LinkedHashSet

LinkedHashSet实现类

HashSet的子类,遍历其内部数据时,可以按照添加的顺序遍历,(但存储时是无序的

LinkedHashSet 是 HashSet 的子类

LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置, 但它同时使用双向链表维护元素的次序,这使得元素看起来是以插入 顺序保存的。

LinkedHashSet插入性能略低于 HashSet,但在迭代访问 Set 里的全 部元素时有很好的性能。

LinkedHashSet 不允许集合元素重复。

 对于频繁的遍历LinkedHashSet效率更高

在这里插入图片描述





实现类3:TreeSet
TreeSet实现类

TreeSet 是 SortedSet 接口的实现类,TreeSet 可以确保集合元素处于排序状态。

TreeSet底层使用红黑树结构存储数据

在这里插入图片描述

 新增的方法如下: (了解)

 Comparator comparator() 

Object first() Object last() 

Object lower(Object e) 

Object higher(Object e)

 SortedSet subSet(fromElement, toElement)

 SortedSet headSet(toElement) 

SortedSet tailSet(fromElement) 

TreeSet 两种排序方法:自然排序和定制排序。默认情况下,TreeSet 采用自然排序。

排 序—自然排序

  1. 自然排序:TreeSet 会默认调用集合元素的 compareTo(Object obj) 方法比较元 素之间的大小关系,然后将集合元素按升序(默认情况)排列

由源码分析compareTo(Object o)方法,并实现

  1. 如果试图把一个对象添加到 TreeSet 时,则该对象的类必须实现 Comparable 接口。
  2. TreeSet在遍历时会自动排序(自然排序或定制排序),但一般自然排序无法处理自定义类的对象(往往是因为有很多自定义的属性),需要在自定义类中重写方法

由源码分析compareTo(Object o)方法,并实现

@Override
    public int compareTo(Object o) {
        if (o instanceof User){
            User user=(User) o;
            //定义只比较name属性
            int compare=-this.name.compareTo(user.name);
            //等于-1为小于,等于0为等于,等于1为大于
            if (compare!=0){
                return compare;
            }else {
                //年龄从小到大排列
                return Integer.compare(this.age,user.age);
            }
        }else {
            throw new RuntimeException("输入的类型不匹配");
        }

    }

TreeSet里面比较相同使用的是compareTo,而不用equals()

排 序—定制排序

TreeSet的自然排序要求元素所属的类实现Comparable接口,如果元素所属的类没 有实现Comparable接口,或不希望按照升序(默认情况)的方式排列元素或希望按照 其它属性大小进行排序,则考虑使用定制排序定制排序,通过Comparator接口来 实现。需要重写compare(T o1,T o2)方法。

利用int compare(T o1,T o2)方法,比较o1和o2的大小:如果方法返回正整数,则表 示o1大于o2;如果返回0,表示相等;返回负整数,表示o1小于o2。

要实现定制排序,需要将实现Comparator接口的实例作为形参传递给TreeSet的构 造器。

此时,仍然只能向TreeSet中添加类型相同的对象。否则发生ClassCastException异 常。

使用定制排序判断两个元素相等的标准是:通过Comparator比较两个元素返回了0

   //TreeSet:不能添加不同类的对象
    @Test
    public void test5(){
        Comparator com=new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                if (o1 instanceof User&& o2 instanceof User){
                    User user1=(User) o1;
                    User user2=(User) o2;
                    
                    //从小到大排序
                    return Integer.compare(user1.getAge(), user2.getAge());

                }else {
                    throw new RuntimeException("不匹配");
                }
            }
        };//重写了方法则创建了接口对应实现类!!!!!

        TreeSet set =new TreeSet(com);

        set.add(new User("Tom",13));
        set.add(new User("Tam",22));
        set.add(new User("Tcm",43));
        set.add(new User("Tcm",13));
        set.add(new User("Tdm",34));
        set.add(new User("Tem",51));

        Iterator iterator =set.iterator();

        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }

特殊声明:本人并不是大佬,只是说一说自己在学习Java过程中的理解,文章有不正确的地方欢迎大家批评指正。
温馨提示:道路千万条,学习第一条。平时不学习,招聘两行泪!!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值