一、Set的介绍
Set是一个接口,存储无序的、不可重复的元素
其实现类有以下3个:
1、HashSet
(常用)
- 线程不安全的
- 可存储null值
2、LinkedHashSet
- 是HashSet的子类,由于是链表结构,遍历其内部数据时,可以按照添加的顺序读取
3、TreeSet
- 可以按照添加对象的指定属性进行排序(实现Comparable或者是Comparator)
二、HashSet
1、特点
- 不能保证元素的排列顺序
- 不是线程安全
- 集合元素可以是null
2、要求
- 判断两个元素相等:两个对象通过hashCode()方法比较相等,并且两个对象通过equals()方法也比较相等
- 对于存放在Set容器中的对象,对象的类一定要重写hashCode()和equals()方法。同时保证如果根据equals()方法,两个对象是相等的,那么在两个对象上调用hashCode()方法也必须生成相同的整数结果
3、构造方法
4、基本方法
示例:
Person类:
package SetPack;
public class Person {
private String name;
private int age;
public Person() {};
public Person(String name,int age)
{
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 boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
if (age != person.age) return false;
return name != null ? name.equals(person.name) : person.name == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + age;
return result;
}
}
主函数:
package SetPack;
import java.util.*;
public class HashSetDemo {
public static void main(String[] args) {
Set<Person> set=new HashSet<>();
set.add(new Person("DaMing",18));
set.add(new Person("LingLing",20));
set.add(new Person("Zhangsan",50));
set.add(new Person("LingLing",20));
Iterator<Person> it=set.iterator();
while(it.hasNext())
{
Person tmp=it.next();
System.out.println(tmp.getName()+tmp.getAge());
}
}
}
由此看出,重写了hashCode()方法和equals()方法后,就能保证元素不重复了。
三、LinkedHashSet
1、特点
-
LinkedHashSet
是由可预知迭代顺序的Set接口的哈希表和链接列表实现。此链接列表定义了迭代顺序,即按照元素插入到集合中的顺序进行迭代。 - 需要注意的是,插入顺序不受在集合中重新插入的元素的影响。(如果在 s.contains(e) 返回 true 后立即调用 s.add(e),则元素 e 会被重新插入到集合 s 中。)
2、构造方法
3、基本方法
示例:
package SetPack;
import java.util.*;
public class LinkedHashSetDemo {
public static void main(String[] args) {
Set<Person> set=new HashSet<>();
set.add(new Person("DaMing",18));
set.add(new Person("LingLing",20));
set.add(new Person("Zhangsan",50));
set.add(new Person("LingLing",20));
System.out.println("--------------------------------------输出HashSet--------------------------------------------");
Iterator<Person> it=set.iterator();
while(it.hasNext())
{
Person tmp=it.next();
System.out.println(tmp.getName()+tmp.getAge());
}
Set<Person> LinkedSet=new LinkedHashSet<>();
LinkedSet.add(new Person("DaMing",18));
LinkedSet.add(new Person("LingLing",20));
LinkedSet.add(new Person("Zhangsan",50));
LinkedSet.add(new Person("LingLing",20));
System.out.println("---------------------------------------输出LinkedHashSet--------------------------------------");
Iterator<Person> it2=LinkedSet.iterator();
while(it2.hasNext())
{
Person tmp=it2.next();
System.out.println(tmp.getName()+tmp.getAge());
}
}
}
运行结果:
可以看到,LinkedHashSet的输出顺序和插入的顺序是一样的,并且在最后依次执行LinkedSet.add(new Person("LingLing",20));
语句也并没有改变它插入的位置,即插入顺序不受在集合中重新插入的元素的影响。
虽然输出顺序和插入顺序一致,但是仍然不能说LinkedHashSet是有序的,因为存储到内存中时,是根据某一个hash函数确定插入的位置,因此在内存中数据的顺序是无序的,在这里只是输出顺序和插入顺序一致,不能说明其就是有序的。因此判断是否有序,是根据其在内存中是否有序。
四、TreeSet
TreeSet类保证排序后的 set 按照升序排列元素,根据使用的构造方法不同,可能会按照元素的自然顺序 进行排序(参见 Comparable),或按照在创建 set 时所提供的比较器(Comparator)进行排序。
- 比较两个对象是否相同的标准看的是实现的比较器返回值,如果返回0,则不插入。
1、实现了Comparable的TreeSet
示例:
package SetPack;
import java.util.*;
public class TreeSetDemo {
public static void main(String[] args) {
Set<Integer> treeset=new TreeSet<>();
treeset.add(3);
treeset.add(30);
treeset.add(13);
treeset.add(5);
treeset.add(9);
System.out.println("输出数据元素为Integer类型时的TreeSet:");
Iterator<Integer> it=treeset.iterator();
while(it.hasNext())
{
System.out.print(it.next()+"\t");
}
System.out.println();
//需要重写Comparable接口
Set<Person> setPerson=new TreeSet<>();
setPerson.add(new Person("Daming",18));
setPerson.add(new Person("Ali",16));
setPerson.add(new Person("Ben",19));
setPerson.add(new Person("Betty",15));
setPerson.add(new Person("Tom",26));
setPerson.add(new Person("Jim",23));
setPerson.add(new Person("Jim",32));
setPerson.add(new Person("Jim",32));
System.out.println("输出数据元素为Person类型时的TreeSet:");
Iterator<Person> it2=setPerson.iterator();
while(it2.hasNext())
{
Person tmp=it2.next();
//重写toString方法
System.out.println(tmp.toString());
}
}
}
Comparable接口的实现如下:
@Override
public int compareTo(Person o) {
int res=this.name.compareTo(((Person)o).name);
if(res!=0)
{
return res;
}
return this.age-((Person)o).age;
}
先按照姓名从小到达排序,姓名相同再按照年龄从小到大排序。
运行结果:
2、实现Comparator的TreeSet
示例:
package SetPack;
import java.util.*;
public class TreeSetDemo2 {
public static void main(String[] args) {
Comparator com=new Comparator<Person>(){
public int compare(Person p1,Person p2)
{
if(p1.getAge()-p2.getAge()!=0)
{
return p1.getAge()-p2.getAge();
}
else
{
return p1.getName().compareTo(p2.getName());
}
}
};
Set<Person> setPerson=new TreeSet<>(com);
setPerson.add(new Person("Daming",18));
setPerson.add(new Person("Ali",16));
setPerson.add(new Person("Ben",19));
setPerson.add(new Person("Betty",15));
setPerson.add(new Person("Tom",26));
setPerson.add(new Person("Jim",23));
setPerson.add(new Person("Jim",32));
setPerson.add(new Person("Jim",32));
System.out.println("输出数据元素为Person类型时的TreeSet:");
Iterator<Person> it2=setPerson.iterator();
while(it2.hasNext())
{
Person tmp=it2.next();
//重写toString方法
System.out.println(tmp.toString());
}
}
}
Comparator的实现:
Comparator com=new Comparator<Person>(){
public int compare(Person p1,Person p2)
{
if(p1.getAge()-p2.getAge()!=0)
{
return p1.getAge()-p2.getAge();
}
else
{
return p1.getName().compareTo(p2.getName());
}
}
};
先按照年龄从小到大,年龄相同则按照姓名从小到大
运行结果:
在这里,同时在Person也实现了Comparable接口。但是由于Set<Person> setPerson=new TreeSet<>(com);
,将Comparator的实现写到了参数里,因此是按照Comparator的规则来排序。