修炼完这一篇秘籍,你就可以成为六段斗之气战士了,天赋出色者甚至可以越阶战斗,成为百里挑一的斗者,踏上漫漫强者路,拳打北山幼儿园,脚踢南海敬老院。
该系列文章上一篇: 四段斗之气–Collection接口.
该系列文章下一篇: 突飞猛进九段斗之气–Map接口.
文章目录
和Set接口的初恋故事
- Set接口是Collection的子接口,Set接口没有提供额外的方法。
- Set集合不允许包含有相同的元素,如果试着把两个相同的元素加入同一个Set集合中,添加操作会失败。
- 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将会把它们存储在不同的位置,但依然可以添加成功。
戏谈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里面的全部元素时会因为链表结构有更好的性能。
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.
更多
对我的文章感兴趣,持续更新中…