java集合框架详解(二)、set接口
来源:互联网 发布:c语言volatile的用法 编辑:程序博客网 时间:2024/06/06 00:01
3.1.1 Set接口
首先我们应该知道Set是Collection接口的子接口
Set和Collection基本是一样的,但一点除外:Set无法记住添加的顺序,不允许包含重复的元素。一个不包含重复元素的 collection。更确切地讲,set 不包含满足e 1.equals(e 2)的元素对e 1和e 2,并且最多包含一个 null 元素。
有几点说明:
1、当试图添加两个相同元素进Set集合,添加操作会失败,add()方法返回false。
2、Set判断两个对象是否相等用equals,而不是使用==。
也就是说两个对象equals比较返回true,Set集合是不会接受这个两个对象的。
我们从JDK API中看到Set接口的所有已知实现类有:
AbstractSet, ConcurrentSkipListSet, CopyOnWriteArraySet, EnumSet, HashSet, JobStateReasons, LinkedHashSet, TreeSet
其中最常用的子类:a、HashSet:散列存放 b、TreeSet:有序存放
在详细介绍两个最长用子类之前,我们有必要明白HashSet与TreeSet是两种不同的数据存放格式,对应着不同的存入数据方式和取数据方式
a、HashSet:散列存放
HashSet类是Set接口最常用的实现类,采用hash算法存储数据,具有良好的存储和查找功能。做如下几点说明:
1、它散列存储,不记录添加顺序。
2、排列顺序时,顺序有可能发生变化。同时线程不安全的,多个线程访问一个HashSet要使用同步代码。
3、HashSet集合元素值允许是null,但是最多只能有一个;
4、hash(翻译为哈希,或散列)算法的功能:
保证通过一个对象快速找到另一个对象,其算法价值体现在速度,可以保证查询快速执行。
当从HashSet中访问元素时,HashSet先计算该元素的hashCode(也就是该对象的hashCode方法返回值),然后直接到该HashCode对应的位置取出该元素。在这里对象的hashCode就好比是数组里的索引,但是不是索引。对于不同类型字段如何取得hashCode见下表(我们在程序实例中会说明如何在eclipse自动添加):
接着讲述HashSet元素添加。 当向HashSet集合中存入一个元素时,HashSet首先会调用该对象的hashCode()方法(是类Object的方法)来得到该对象的hashCode值,然后判断已经存储在集合中的对象的hashCode值是否与添加的对象的hashCode值一致:
若不一致:直接添加进去;
若一致:再进行equals方法(是父接口Collection中的方法)比较,equals方法如果返回true,表明对象已经添加进去了,就不会再添加新的对象了,否则添加进去。
如果我们重写了equals方法,也要重写hashCode方法,反之亦然。
注意:
1、HashSet集合判断两个元素相等的标准是两个对象通过equals方法比较相等,并且两个对象的hashCode方法返回值也相等。
2、如果需要某个类的对象保存到HashSet集合中,覆写该类的equals()和hashCode()方法,应该尽量保证两个对象通过equals比较返回true时,他们的hashCode返回也相等。
下表是HashSet类得方法列表:由于方法比较简单,我们将不会在程序中详细演示(可对照上篇中的方法演示)
方法摘要
boolean
add(E e)
如果此 set 中尚未包含指定元素,则添加指定元素。
void
clear()
从此 set 中移除所有元素。
Object
clone()
返回此 HashSet 实例的浅表副本:并没有复制这些元素本身。
boolean
contains(Object o)
如果此 set 包含指定元素,则返回 true。
boolean
isEmpty()
如果此 set 不包含任何元素,则返回 true。
Iterator<E>
iterator()
返回对此 set 中元素进行迭代的迭代器。
boolean
remove(Object o)
如果指定元素存在于此 set 中,则将其移除。
int
size()
返回此 set 中的元素的数量(set 的容量)。
我们来看第一个程序实例:参照代码理解上述HashSet的知识重点。
import java.util.HashSet;import java.util.Set;/** * HashSet添加元素: *Set set = new HashSet(); *set.add("tyy"); *先调用"tyy".hashCode(),判断set集合里是否已经有了"tyy".hashCode(): *两种可能:1.元素tyy的hashCode不存在: 就直接把tyy添加到set集合; * 2.元素tyy的hashCode存在: 再比较元素"tyy"和与那个与元素"tyy"具有一样的Hashcode值的元素对象, 使用对象的equals比较。比较结果有两张,分别是false与true, false: 就添加进set; true: 表明是一个对象,add(Object e),就返回false,添加失败 *///下面的方法hashCode()与eaquals()方法是用来对HashSet集中的元素进行判断的//AA类仅仅覆写hashCode()方法class AA{public int hashCode() {return 0;}}//BB类仅仅覆写equals(Object obj)方法class BB{public boolean equals(Object obj) {return true;}}//CC类即覆写hashCode()同时也覆写equals(Object obj)/*接着向HashSet中添加元素。 当向HashSet集合中存入一个元素时,HashSet首先会调用该对象(此时要添加的元素)的hashCode()方法(是 * 类Object的方法)来得到该对象的hashCode值,然后判断已经存储在集合中的对象的hashCode值是否与添加的对象的hashCode值一致: *若不一致:直接添加进去; *若一致:再进行equals方法(是父接口Collection中的方法)比较,equals方法如果返回true,表明对象已经添加进 *去了,就不会再添加新的对象了,否则添加进去。如果我们重写了equals方法,也要重写hashCode方法,反之亦然。*///如果需要某个类的对象保存到HashSet集合中,覆写该类的equals()和hashCode()方法class CC{public int hashCode() {return 10;}public boolean equals(Object obj) {return true;}}public class HashSetDemo {public static void main(String[] args) {Set set = new HashSet();//创建一个HashSet对象setboolean b = set.add("tyy");//添加一个元素tyySystem.out.println(b);//trueb = set.add("tyy");//再次添加元素tyySystem.out.println(b);//false,这里是false的原因是,当添加两个相同元素进Set集合,由于他们两的hashCode相等//所以添加操作会失败,add()方法返回false。set.add(null);set.add(null);System.out.println(set);//[null, tyy] 。已经添加过一个null了,set集最多存储一个nullset.clear();System.out.println("===================");set.add(new AA());set.add(new AA());set.add(new BB());set.add(new BB());set.add(new CC());set.add(new CC());System.out.println(set);}}
本段程序的运行结果如下
true
false
[null, tyy]
===================
[collection.AA@0,collection.AA@0,collection.BB@e95a56,collection.BB@1632847, collection.CC@a]
我们分析一下结果,显然前四行的结果我们已在程序注释中给出解释了。5、6两行的结果原因是,在创建类元素AA与BB存储到hashSet集中时,要对元素进行hashCode与equals判断,而在类AA与BB中只是显示的覆写了两个判断方法中的一个,这也就是为什么AA、BB会类元素在结果中会出现两个,而CC只是一个。
接着我们看下一个程序示例:import java.util.HashSet;import java.util.Set;class Stu{private long id;private String name;//对学生的ID号进行hashCode与equals判断,以区别学生。//点击鼠标右键,选择源码,选择生产hashCode与equals,选择生产的字段,OK@Overridepublic int hashCode() {final int prime = 31;int result = 1;result = prime * result + (int) (id ^ (id >>> 32));return result;}@Overridepublic boolean equals(Object obj) {if (this == obj)return true;if (obj == null)return false;if (getClass() != obj.getClass())return false;Stu other = (Stu) obj;if (id != other.id)return false;return true;}}public class HashSetDemo1 {public static void main(String[] args) {Set set = new HashSet();set.add(new Stu());}}
b、TreeSet:有序存放
这里我们进入JDK API文档,在Treeset类中,我们可以看到它有下面所示的构造方法:
TreeSet()
构造一个新的空 set,该 set 根据其元素的自然顺序进行排序。
TreeSet(Comparator<? super E> comparator)
构造一个新的空 TreeSet,它根据指定比较器进行排序
实际上它有四个构造方法,我们只看这两个。默认情况构造一个新的空set集时,该set集根据其元素的自然顺序进行排序(从小到大),就是构造方法一。而对于构造方法二,它是供我们自己设置排序方法的(比如我们可以设置成从大到小的顺序)
一而言之:TreeSet类使用元素的自然顺序对元素进行排序,或者根据创建 set 时提供的 Comparator 进行排序。
注意:参与排序的元素必须是同一类型的,不然会发生ClassCastException异常。
看示例:TreeSetDemo1
import java.util.Set;import java.util.TreeSet;public class TreeSetDemo1 {public static void main(String[] args) {//构造一个新的空 set,该 set 根据其元素的自然顺序进行排序。Set set = new TreeSet();// 默认的比较就是 自然顺序,(升序) set.add("大哥");set.add("小姐");set.add("BB");set.add("AA");set.add("bb");set.add("aa");set.add("1");set.add("2");System.out.println(set);}}
程序结果:[1, 2, AA, BB, aa, bb, 大哥, 小姐],可以看出它的输出顺序是什么样子的
TreeSet与HashSet相比额外的方法有:
Comparator comparator():返回当前Set使用的Comparator,若返回null,表示以自然顺序排序。
Object first() 返回此 set 中当前第一个(最低)元素。
Object last() 返回此 set 中当前最后一个(最高)元素。
SortedSet subSet(Object fromElement, E toElement) 返回此 set 的部子集,其元素从 fromElement(包括)到 toElement(不包括)。
SortedSet headSet(Object toElement)返回此 set 的部分子集,其元素严格小于 toElement。
SortedSet tailSet(Object fromElement) 返回此 set 的部分子集,其元素大于等于 fromElement。
TreeSet的排序之自然排序
TreeSet会调用元素的compareTo(Object o)方法来比较元素之间的大小关系,然后将集合里的元素按升序排列。此时需要排序元素的类必须实现Compareble接口。并覆写其int compareTo(Object o)方法。
compareTo(Object o)方法简介:接口Compareble中方法,比较此对象“this”与指定对象“o”的顺序。如果该对象小于、等于或大于指定对象,则分别返回负整数、零或正整数。
对于TreeSet集合而言,判断两个对象相等的标准是: compareTo()方法比较返回 0
示例:
import java.util.Set;import java.util.TreeSet;/** * 根据人的年龄来排序,升序 * */class Person implements Comparable {private int age; // private Integer high;//high这里是引用数据类型public Person(int age) {this.age = age;//this.high= high;}public int compareTo(Object o) {if (o instanceof Person) {Person p = (Person) o; if (this.age > p.age) {return 1;} else if (this.age < p.age) {return -1;}} return 0;//如果排序字段是引用数据类型的话//if (o instanceof Person) {//判断o是否是属于Person//Person p = (Person) o;//return this.high.compareTo(p.high);//}//return 0;}public String toString() {return this.age + "";}}public class TreeSetDemo2 {public static void main(String[] args) {//构造一个新的空 set,该 set 根据其元素的自然顺序进行排序。Set set = new TreeSet();/* * 默认的比较就是 自然顺序,(升序);要排序必须实现java.lang.Comparable接口 再覆写该接口里的 * compareTo(Objec o){ */set.add(new Person(17));set.add(new Person(28));set.add(new Person(56));set.add(new Person(31));System.out.println(set);}}
程序运行结果:[17, 28, 31, 56]
TreeSet的排序之定制排序
TreeSet的自然排序是根据元素的大小进行升序排序的,若想自己定制排序,比如降序排序,就可以使用Comparator接口(此接口是 Java Collections Framework 的成员)了。该接口包含int compare(Object o1,Object o2)方法,用于比较两个对象的大小,比较结果和compareTo方法一致。
示例:
import java.util.Set;import java.util.TreeSet;public class TreeSetDemo3 {public static void main(String[] args) {//构造一个新的空 set,该 set 根据其元素的自然顺序进行排序。Set set = new TreeSet();set.add(17);set.add(-2);set.add(95);set.add(4);TreeSet s = (TreeSet)set;//set不能直接调用Comparator()方法,必须要先把set强转型为TreeSet类型/* * Comparator<? super E> comparator() 返回对此 set 中的元素进行排序的比较器;如果此 set 使用其元素的自然顺序,则返回 null。 * */System.out.println(s.comparator());System.out.println(s.first());System.out.println(s.last());/** 这里上下都是treeset类中的方法 * SortedSet<E> headSet(E toElement) 返回此 set 的部分视图,其元素严格小于 toElement。 */System.out.println(s);System.out.println(s.headSet(17));System.out.println(s.tailSet(4));}}
上述程序中的方法详细参见TreeSet类
程序运行结果:
-2
95
[-2, 4, 17, 95]
[-2, 4]
[4, 17, 95]
要实现定制排序,需要在创建TreeSet集合对象时,提供一个Comparator对象(TreeSet(Comparator comparator) ),该对象里负责集合元素的排序逻辑。
示例:
import java.util.Comparator;import java.util.Set;import java.util.TreeSet;/** * 根据人的年龄来排序,降序 * */class Person2 {private Integer age;public Person2(int age) {this.age = age;}public Integer getAge() {return age;}public String toString() {return this.age + "";}}class MyComparator implements Comparator{//在这里写比较规则public int compare(Object o1, Object o2) {Person2 p1 = (Person2) o1;Person2 p2 = (Person2) o2;if(p1.getAge() > p2.getAge()){return -1;}else if(p1.getAge() < p2.getAge()){return 1;}return 0;}}public class ComparatorDemo {public static void main(String[] args) {Set set = new TreeSet(new Comparator() {//new TreeSet(new MyComparator());//在这里写比较规则,用的是匿名内部类public int compare(Object o1, Object o2) {Person2 p1 = (Person2) o1;Person2 p2 = (Person2) o2;if(p1.getAge() > p2.getAge()){return -1;}else if(p1.getAge() < p2.getAge()){return 1;}return 0;}});set.add(new Person2(23));set.add(new Person2(13));set.add(new Person2(03));set.add(new Person2(43));set.add(new Person2(93));System.out.println(set);}}
未完待续......
注:本文是参考众多资料,感谢前辈的支持。
- java集合框架详解(二)、set接口
- 集合框架二(Set接口)
- Java集合框架(二)之Set详解
- Java集合框架详解之继承set接口
- 黑马程序员——JAVA基础------集合框架(二)----Set接口
- java集合框架系列---Set接口
- Java复习之集合框架Set接口
- JAVA集合框架之List Set接口
- 集合框架-Set接口
- java日常学习:集合(二)list和set接口
- Java-集合框架Collection之Set(二)
- Java基础之集合框架详解(三)Set篇
- Java:集合框架(三)Set详解及代码示例
- JAVA常用集合框架用法详解基础篇三之Colletion子接口Set
- java 集合框架(3)Set接口和SortedSet接口
- java集合-Set接口
- Java基础---集合框架二(ArrayList & 接口)
- Java集合框架_二_Iterator接口
- NSURL详解
- Datepicker用法
- Ubuntu 14.04一步一步安装Openstack Kilo版本-9(nova)
- iOS网络
- Calendar的基本使用
- java集合框架详解(二)、set接口
- 源码阅读 5 微信6.0底部效果
- iOS音频技术
- yii2文件上传
- android数据存储读取1:SharedPreferences(对比IOS)
- Listview异步加载图片之优化篇(有图有码有解释)
- iOS屏幕旋转总结
- angularjs 简介
- Ubuntu 14.04一步一步安装Openstack Kilo版本-10(neutron)