这一篇我们来介绍Collection接口的另一个子接口,Set接口。Set是个接口,元素不可以重复,是无序的。Set接口中的方法和Collection的一致。
A、Set的子类:
1、HashSet:此类实现的Set接口,由哈希表(实际上是一个HashMap)实例支持,它不保证Set的迭代顺序,但是允许使用null元素。内部结构是哈希表,是不同步的。
哈希表确定元素是否相同的步骤如下:
一、 判断的是两个元素的哈希值(hashcode)是否相同。如果相同,再判断两个对象的内容是否相同,如果内容相同就表示两个元素是相同的。
二、 判断哈希值是否相同,其实判断的是对象的hashCode()的方法。判断内容是否相同,用的是equals()方法。
三、 如果哈希值不同,那就不需要进行equals判断。
HashSet集合数据结构是哈希表,所以存储元素的时候,使用元素的hashCode()方法来确定位置,如果位置相同,再通过元素的equals方法来确定是否相同。
练习题:定义功能去除ArrayList的重复元素?
注意:像ArrayList集合,在判断元素是否相同的时候,仅仅需要判断equals()方法。因为数据结构不同,对元素的判断依据也就一样。
代码如下:
package com.wq.person;
public class Person extends Object {
private String name;
private int age;
public Person() {
super();
// TODO Auto-generated constructor stub
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof Person))
return false;
Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public String toString() {
// TODO Auto-generated method stub
return name+"...."+age;
}
}
//hashset测试程序
package com.wq.hashset;
import java.util.HashSet;
import java.util.Iterator;
import com.wq.person.Person;
public class testHashSet {
public static void main(String[] args) {
// TODO Auto-generated method stub
HashSet hs=new HashSet();//实例化一个hashset对象
hs.add(new Person("lisi1",21));
hs.add(new Person("lisi2",22));
hs.add(new Person("lisi3",23));
hs.add(new Person("lisi4",24));
hs.add(new Person("lisi4",24));//如果想在迭代中不出现相同的元素,需要通过覆写Person类的hashcode和equals方法
//使用迭代器进行迭代
for(Iterator it=hs.iterator();it.hasNext();){
Person pe=(Person)it.next();
System.out.println(pe.getName()+"---"+pe.getAge());
}
}
}
运行结果如下(由于无序这只是一种可能):
lisi4---24
lisi3---23
lisi2---22
lisi1---21
特别注意:如果希望元素不重复,但是要有序,这时可以考虑使用LinkedHashSet,它是HashSet的子接口。
2、TreeSet接口,可以对Set集合的元素进行排序,不是同步的。它底层的数据结构是红黑树。
判断元素唯一性的方式就是根据比较方法compareTo()的返回结果,判断返回的结果是否为0,是0就表示相同的元素且不会存储在Set中的(Set是不允许存储重复的元素的)。如果返回的结果不是0,则表示的是不相同。
TreeSet对元素进行排序的方式一:让元素具备比较功能,元素需要实现comparable接口,覆盖CompareTo()方法。
如果不要按照对象具备的自然顺序进行排序,如果对象不具备自然顺序。(即对象没有比较性,或者具备的比较性不是我们所需要的)这个时候怎么办???
TreeSet对元素进行排序的方式二:(生成一个比较器)让集合自身具备比较的功能,定义一个类,实现Comparator接口,覆盖compare方法,将该类的对象作为参数传递给TreeSet集合的构造函数。
代码演示如下:
//Person类
package com.wq.person;
//需要实现Comparable接口
public class Person extends Object implements Comparable{
private String name;
private int age;
public Person() {
super();
// TODO Auto-generated constructor stub
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof Person))
return false;
Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public String toString() {
// TODO Auto-generated method stub
return name+"...."+age;
}
//需要覆写的compareTo方法
//这里我的思路是先比较age的大小,如果age相等,则
//接着比较name的字典顺序大小
@Override
public int compareTo(Object o) {
// TODO Auto-generated method stub
Person per=(Person) o;//实现强制类型转换
if(this.age>per.age){
return 1;
}else if(this.age<per.age){
return -1;
}else{//如果年纪相等就比较名字的字典顺序大小
int temp=this.name.compareTo(per.name);
return temp;
}
}
}
//自己写的Comparator接口
package com.wq.comparator;
import java.util.Comparator;
import com.wq.person.Person;
public class ComparatorByName implements Comparator {
/**
* 覆写compare方法,生成一个比较器。这里我们用name来作为比较
*/
@Override
public int compare(Object o1, Object o2) {
// TODO Auto-generated method stub
Person p1=(Person)o1;
Person p2=(Person)o2;
int temp=p1.getName().compareTo(p2.getName());//获取两个对象的name比较的结果
return temp==0?p1.getAge()-p2.getAge():temp;
}
}
//测试类。用于测试CompareTo()方法和Comparable接口
package com.wq.TreeSet;
import java.util.Iterator;
import java.util.TreeSet;
import com.wq.comparator.ComparatorByName;
import com.wq.person.Person;
public class testTreeSet {
public static void main(String[] args) {
// TODO Auto-generated method stub
//测试实现Comparable接口的比较功能
System.out.println("测试实现Comparable接口的比较功能");
testComparableDemo();
System.out.println("测试实现Comparator接口,作为一个比较器来进行比较的功能");
//测试实现Comparator接口,作为一个比较器来进行比较的功能
testComparatorDemo();
}
public static void testComparatorDemo() {
TreeSet ts=new TreeSet(new ComparatorByName());//实例化对象,并且确定比较器
//添加自定义对象
//在添加元素时,自定义的对象必须实现Comparator接口,同时要覆盖compare方法
//否则就会报错
ts.add(new Person("zhangsan",22));
ts.add(new Person("zhaoliu",24));
ts.add(new Person("zhouqi",25));
ts.add(new Person("wuba",28));
ts.add(new Person("lisi",26));
ts.add(new Person("wangwu",25));
for(Iterator it=ts.iterator();it.hasNext();){
Person per=(Person)it.next();
System.out.println(per);
}
}
public static void testComparableDemo() {
TreeSet ts=new TreeSet();//实例化对象,
//添加自定义对象
//在添加元素时,自定义的对象必须实现Comparable接口,同时要覆盖compareTo方法
//否则就会报错
ts.add(new Person("zhangsan",22));
ts.add(new Person("zhaoliu",24));
ts.add(new Person("zhouqi",25));
ts.add(new Person("wuba",28));
ts.add(new Person("lisi",26));
ts.add(new Person("wangwu",25));
for(Iterator it=ts.iterator();it.hasNext();){
Person per=(Person)it.next();
System.out.println(per);
}
}
}
运行结果如下:
测试实现Comparable接口的比较功能(以年龄大小)
zhangsan....22
zhaoliu....24
wangwu....25
zhouqi....25
lisi....26
wuba....28
测试实现Comparator接口,作为一个比较器来进行比较的功能(以名字的字典顺序)
lisi....26
wangwu....25
wuba....28
zhangsan....22
zhaoliu....24
zhouqi....25
练习:TreeSet集合练习,字符串长度的排序
分析:Java代码中。TreeSet默认的会为字符串进行字典顺序大小的排序,但这不符合我们的要求。这个时候怎么办,我们必须要用到比较器。即comparator接口来实现。
具体代码实现如下:
//自定义的比较器
package com.wq.comparator;
import java.util.Comparator;
import com.wq.person.Person;
/*
* 生成一个比较器,用来比较字符串的长度大小,如果长度相等则比较字符的字典顺序大小
*/
public class ComparatorByLength implements Comparator {
@Override
public int compare(Object o1, Object o2) {
// TODO Auto-generated method stub
String str1=(String)o1;
String str2=(String)o2;
int temp=str1.length()-str2.length();
return temp==0?str1.compareTo(str2):temp;
}
}
//测试类
package com.wq.TreeSet;
import java.util.Iterator;
import java.util.TreeSet;
import com.wq.comparator.ComparatorByLength;
import com.wq.person.Person;
/**
* 这个练习是对TreeSet集合的练习
* 主要是进行字符串的长度的排序
* @author LULEI
*
*/
public class testTreeSetDemo {
public static void main(String[] args) {
// TODO Auto-generated method stub
TreeSet ts=new TreeSet(new ComparatorByLength());//在构造函数中添加相应的构造器
ts.add("asdfdf");
ts.add("dds");
ts.add("fghf");
ts.add("as");
ts.add("t");
ts.add("fdsfsg");
for(Iterator it=ts.iterator();it.hasNext();){
Object o=(Object)it.next();
System.out.println(o);
}
}
}
运行结果如下:
t
as
dds
fghf
asdfdf
fdsfsg
--------------------------------------------------------------------*--------------------------------------------------------------------------------------------------
我们已经介绍了List和Set接口,接下来总结一下。
总结:List: 1---ArrayList 2---LinkedList
Set 1---HashSet --LinkedHashSet
2---TreeSet
注意,后缀名就是该集合所属的体系,前缀名就是该集合的数据结构。
1、 看到array:就要想到数组,就要想到查询快,有角标。如果不希望出现重复的元素则需要覆盖equals()方法。
2、看到link就要想到链表,就要想到增删快,就要想到add/get/remove+first(last)的方法。
3、看到hash就要想到哈希表,就要想到唯一性,就要想到元素需要覆盖hashCode()和equals()方法。
4、看到tree就要想到二叉树,就要想到排序,就要想到两个接口:comparator和comparable。
并且通常这些常用的容器都是不同步的。