Set接口的特性:Set接口继承了Collection接口,Set集合中不能包含重复的元素,每个元素必须是唯一的,且集合中的元素是无序的。
Set接口的三个实现类:
一、HashSet类:
HashSet的实现原理其实是HashMap,只不过它的key也是value。采用hash表算法来实现,存放的元素是不重复,且无序的。
如何判断两个元素重复?
通过hashCode和equals方法来保证元素的唯一性,add方法返回的是boolean类型。
首先,通过判断元素的hashCode值是否一致,只有在该值相同的情况下,才会进行equals判断。如果存储在HashSet中的两个对象HashCode值和equals方法返回的结果都相同时,那么认为这两个元素是相同元素,只能存储一个。
注意:hashCode值相同,而equals不相同的两个元素是不同的,只不过通过存储在同一个hashCode位置上而已。如图2:

应用场景:要一个能快速访问的Set,就使用HashSet。
如果我们添加自定义类的对象到HashSet中时,要重写HashCode方法和equals方法,来确保有相同属性的两个对象具有相同的hashCode值,判断它重复。
二、TreeSet类:
TreeSet是采用树结构实现(称为红黑树算法),元素是有序且不重复的,主要有add,remove,contains方法,时间复杂度都为O(log(n))。还提供一些处理排序的set方法,如first(),headSet(),tailSet().
因为是有序的,所以输入和输出的顺序是不同的,输出是按顺序来输出的。
TreeSet集合排序的两种方式:
- 让元素自身具备比较性:即传入的元素自身要实现Comparable接口,覆盖compareTo方法。
这种方式也作为元素的自然排序,称为默认排序。
public class Demo4 {
public static void main(String[] args) {
TreeSet ts = new TreeSet();
ts.add(new Person("aa", 20, "男"));
ts.add(new Person("bb", 18, "女"));
ts.add(new Person("cc", 17, "男"));
ts.add(new Person("dd", 17, "女"));
ts.add(new Person("dd", 15, "女"));
ts.add(new Person("dd", 15, "女"));
System.out.println(ts);
System.out.println(ts.size()); // 5
}
}
class Person implements Comparable {
private String name;
private int age;
private String gender;
public Person() {
}
public Person(String name, int age, String gender) {
= name;
this.age = age;
this.gender = gender;
}
public String getName() {
return name;
}
public void setName(String name) {
= name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
@Override
public int hashCode() {
return name.hashCode() + age * 37;
}
public boolean equals(Object obj) {
System.err.println(this + "equals :" + obj);
if (!(obj instanceof Person)) {
return false;
}
Person p = (Person) obj;
return .equals() && this.age == p.age;
}
public String toString() {
return "Person [name=" + name + ", age=" + age + ", gender=" + gender
+ "]";
}
@Override
public int compareTo(Object obj) {
Person p = (Person) obj;
System.out.println(this+" compareTo:"+p);
if (this.age > p.age) {
return 1;
}
if (this.age < p.age) {
return -1;
}
return .compareTo(); //当compareTo()函数返回值为0时,说明两个对象相等
}
}- 让容器自身具备比较性,自定义比较器:即当元素自身不具备比较性,或具备的比较性不是所需的时,
那么这时候只能让容器自身具备比较性。
定义一个类实现Comparator接口,覆盖compare方法,并将该类的对象作为参数传递给TreeSet集合的构造函数。
注意:当元素自身的比较器Comparable和容器的比较器Comparator同时存在的时候,以容器的Comparator为主。
public class Demo5 {
public static void main(String[] args) {
TreeSet ts = new TreeSet(new MyComparator()); //该类的对象作为参数传入到TreeSet中
ts.add(new Book("think in java", 100));
ts.add(new Book("java 核心技术", 75));
ts.add(new Book("现代操作系统", 50));
ts.add(new Book("java就业教程", 35));
ts.add(new Book("think in java", 100));
ts.add(new Book("ccc in java", 100));
System.out.println(ts);
}
}
//定义一个类实现Comparator接口,覆盖compare方法
class MyComparator implements Comparator {
public int compare(Object o1, Object o2) {
Book b1 = (Book) o1;
Book b2 = (Book) o2;
System.out.println(b1+" comparator "+b2);
if (b1.getPrice() > b2.getPrice()) {
return 1;
}
if (b1.getPrice() < b2.getPrice()) {
return -1;
}
return b1.getName().compareTo(b2.getName());
}
}
class Book {
private String name;
private double price;
public Book() {
}
public String getName() {
return name;
}
public void setName(String name) {
= name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public Book(String name, double price) {
= name;
this.price = price;
}
@Override
public String toString() {
return "Book [name=" + name + ", price=" + price + "]";
}
}注意:在重写compareTo或compare方法时,必须要明确比较的主要条件相等时,要比较次要条件。
即姓名相同时,要比较年龄是否相同。
三、LinekHashSet类:
介于HashSet和TreeSet之间,也是一个hash表,但它同时维护了一个双链表来记录插入的顺序,基本方法的复杂度为O(1)。
LinkedHashSet dset = new LinkedHashSet();
dset.add(new Apple(7));
dset.add(new Apple(6));
dset.add(new Apple(8));
dset.add(new Apple(10));
dset.add(new Apple(9));
Iterator iterator = dset.iterator();
while (iterator.hasNext()) {
System.out.print(iterator.next() + " ");
}输入和输出的顺序是一样的,因为它有记录插入时的顺序。
记忆:
- 看到array,就要想到角标。
- 看到link,就要想到first,last。
- 看到hash,就要想到hashCode,equals.
- 看到tree,就要想到两个接口。Comparable,Comparator。
















