六段斗之气--Collection的子接口:Set接口

        修炼完这一篇秘籍,你就可以成为六段斗之气战士了,天赋出色者甚至可以越阶战斗,成为百里挑一的斗者,踏上漫漫强者路,拳打北山幼儿园,脚踢南海敬老院。

该系列文章上一篇: 四段斗之气–Collection接口.
该系列文章下一篇: 突飞猛进九段斗之气–Map接口.


和Set接口的初恋故事

  1. Set接口是Collection的子接口,Set接口没有提供额外的方法。
  2. Set集合不允许包含有相同的元素,如果试着把两个相同的元素加入同一个Set集合中,添加操作会失败。
  3. Set判断两个对象是否相同不是使用==运算符,而是根据equals()方法。

Set实现类之一HashSet

  • HashSet是Set接口的典型实现,大多数时候使用Set集合时都使用这个实现类。
  • HashSet按照Hash算法来存储集合中的元素,因此具有很好的存取、查找、删除性能。
  • HashSet具有以下特点:

----不能保证元素的排列顺序
----HashSet不是线程安全的
----集合元素可以是null

  • HashSet集合判断两个元素相等的标准:两个对象通过hashCode()方法比较相等,并且两个对象的equals()方法返回值也相等。
  • 对于存放在Set容器中的对象,对应的类一定要重写equals()和hashCode(Object
    obj)方法,以实现对象相等规则。即:“相等的对象必须具有相等的等列码”。

        对于最后一点,可以先了解一下HashSet中添加元素的过程,实际上该过程和后面HashMap中的添加过程几乎是完全一样的,这里先简单了解一下:当向HashSet集合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据hashCode值通过某种散列函数决定该对象在HashSet底层数组中的存储位置。(这样后面查询的时候就不用按索引一个个去找了,优化了查询速度!)这个散列函数会与底层数组的长度相计算得到在数组中的下标,并且这种散列函数计算还尽可能保证能均匀存储元素,越是散列分布,该散列函数设计的越好。
        如果两个元素的hashCode()值相等,会继续调用equals方法,如果equals方法结果为true,添加失败;如果为false,那么会保存该元素,但是该数组的位置已经有元素了,那么就会以链表的方式继续链接,这样底层结构就变成了一个数组加多个链表,像一把牙齿长短不一的梳子。如果两个元素的equals()方法返回true,但它们的hashCode()返回值不相等,hashSet将会把它们存储在不同的位置,但依然可以添加成功。
HashSet


戏谈hashCode()和equals()方法的重写

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

  • 当一个类有自己特有的”逻辑相等“概念,就绪需要改写equals方法,但是父类Object里面默认的equals方法用的是==号来比较的,比较的是两个对象的地址,假设personA和personB两个对象是同一个人,具有相同的名字和年龄,但你在程序里面分别new出来的这两个对象地址不同,用默认equals方法比较的话是不相等的两个对象,所以需要重写euqals方法,使得它们能够判断成为相等。 这一点实际上在前面Collection接口代码测试中也有体现。
  • 但是,你光重写equals方法肯定不行,因为两个对象地址不同,用默认的hashCode()计算得到的哈希值肯定是不相同的,所以你必须把hashCode()方法也重写。
  • 因此,复写equals()方法的时候一般都需要同时复写hashCode方法。通常参与计算hashCode的对象的属性也因该参与到equals()中进行计算。

Set实现类之二LinkedHashSet

  • LinkedHashSet是HashSet的子类,不允许集合元素重复。
  • LinkedHashSet根据元素的hashCode值来确定元素的存储位置,但它同时使用双向链表维护元素的次序,这使得元素看起来是以插入顺序保存的。
  • LinkedHashSet插入性能略低于HashSet,但是在迭代访问Set里面的全部元素时会因为链表结构有更好的性能。
    LinkedHashSet底层结构示意图

Set实现类之三TreeSet

TreeSet长得什么样
  • TreeSet是SortedSet接口的实现类,TreeSet可以确保集合元素处于排序状态。TreeSet底层使用红黑树结构存储数据,红黑树结构的特点是有序,查询速度比List快,再具体地属于数据结构和算法地基本功了,可以参考

http://www.cnblogs.com/yangecnu/p/Introduce-Red-Black-Tree.html,该文章对红黑树有较为详细地讲解。

自然排序和定制排序

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

  • 自然排序:TreeSet会带调用集合元素的compareTo(Object obj)方法来比较元素之间地大小关系,然后将集合元素按升序(默认情况)排列。如果试图把一个对象添加到TreeSet时,则该对象的类必须实现Comparable接口。实现Comparable接口的类必须实现compareTo(Object obj)方法,两个对象即通过compareTo(Obejct obj)方法的返回值来比较大小。向TreeSet中添加元素时,只有第一个元素无需比较compareTo()方法,后面添加的所有元素都会带哦用compareTo()方法进行比较。因为只有相同类的两个实例才会比较大小,所以向TreeSet中添加的应该是同一个类的对象。对于TreeSet而言,它判断两个对象是否相等的唯一标准是两个对象通过compareTo(Object obj)方法有一致的结果,这意味着如果两个对象通过equals()方法比较返回true时,你需要确保compareTo(Object obj)方法比较应该返回0,否则的话难以理解。
  • 定制排序: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。

六段斗之气什么水平,战斗一下

去重练习
去该系列文章下一篇: 突飞猛进九段斗之气–Map接口.

参考文献

[1]Bruce Eckel.Java编程思想(第4版)[M].机械工业出版社,2008:459-524.

更多

对我的文章感兴趣,持续更新中…

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值