Set 是 Collection 接口的子接口,Set 接口没有提供额外的方法。
Set 类型的集合不允许包含相同的元素,若把两个相同的元素加入同一个 Set 类型的集合中,则添加操作会失败。
Set 类型的集合支持的遍历方式和 Collection 类型的集合一样,可以用 foreach 和 iterator。Set 接口的常用实现类有 HashSet、TreeSet。
Java HashSet实现类
HashSet 是 Set 接口的主要实现类,使用 Set 类型的集合时通常会使用这个实现类。
HashSet 用哈希算法存储集合中的元素,因此具有很好的存储、查找、删除等性能。HashSet 不能保证元素的排列顺序,不是线程安全的,集合元素可以是 null。
【实例】创建集合,使用 HashSet 实现集合中基本的功能,代码如下:
import java.util.HashSet;
public class SetDemo {
public static void main(String[] args) {
SetDemo setDemo = new SetDemo();
setDemo.testSet();
}
public void testSet() {
// 创建 HashSet 集合并添加元素
HashSet hashSet = new HashSet();
hashSet.add("a");
hashSet.add("b");
hashSet.add("c");
System.out.println(hashSet);
}
}
在上述代码中,调用 add() 方法实现了元素添加,使用的是 Collection 接口的方法。在 Set 接口中没有可以修改的方法,可以查看 Collection 接口的方法,直接使用即可。
判断集合中两个元素相等的标准:两个对象通过 hashCode() 方法得到的哈希值相等,且两个对象的 equals() 方法返回值为 true。
对存放在 Set 类型的集合中的对象,对应的类一定要重写 hashCode() 和 equals() 方法,以符合对象相等规则,即“相等的对象必须具有相等的哈希值”。
HashSet 类型的集合中元素的无序性不等同于随机性,这里的无序性与元素的添加位置有关。具体来说,我们在添加每个元素到数组中时,具体的存储位置都是由元素的 hashCode() 调用后返回的哈希值决定的,导致在数组中的每个元素不是依次紧密存放的,表现出一定的无序性。
【实例】创建集合和 Student 类,在 HashSet 类型的集合中判断两个对象是否相等,代码如下:
import java.util.Objects;
public class Student {
private Integer id;
private String name;
private Integer age;
public Student(Integer id, String name, Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
public Student() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return Objects.equals(id, student.id) &&
Objects.equals(name, student.name) &&
Objects.equals(age, student.age);
}
@Override
public int hashCode() {
return Objects.hash(id, name, age);
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
import java.util.HashSet;
public class SetDemo {
public static void main(String[] args) {
Student student = new Student(1, "张三", 20);
Student student02 = new Student(1, "张三", 20);
HashSet hashSet = new HashSet();
hashSet.add(student);
hashSet.add(student02);
System.out.println(hashSet);
}
}
程序运行结果为:
[Student{id=1, name='张三', age=20}]
上述代码创建了 Student 类,并在 Student 类中实现了 hashCode() 方法和 equals() 方法。两个对象通过调用 HashSet 类型集合的 hashCode() 和 equals() 方法来确定元素的唯一性,如果两个对象的 hashCode() 返回值相同,并且 equals() 方法返回 true,HashSet 类型集合就会认为这两个对象是相同的元素,因此只保留其中一个,无法重复添加。
Java TreeSet实现类
TreeSet 是 SortedSet 接口的实现类,可根据指定的属性顺序对集合中的元素。进行遍历,TreeSet 底层使用红黑树结构存储数据。
TreeSet 有两种排序方法,分别是自然排序和定制排序。在默认情况下,TreeSet 采用自然排序。
1) 自然排序
TreeSet 首先调用 compareTo() 方法来比较元素的大小,然后将集合元素按升序(默认情况)排列。
【实例】创建集合和 Student 类,按照对象的年龄从小到大进行排序,采用自然排序方法,代码如下:
import java.util.Comparator;
import java.util.Objects;
public class Student implements Comparable
private Integer id;
private String name;
private Integer age;
public Student(Integer id, String name, Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
public Student() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return Objects.equals(id, student.id) &&
Objects.equals(name, student.name) &&
Objects.equals(age, student.age);
}
@Override
public int hashCode() {
return Objects.hash(id, name, age);
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Object o) {
if (this == o) {
return 0;
}
if (o instanceof Student) {
Student student = (Student) o;
int value = this.age - student.age;
if (value != 0) {
return value;
}
// 年龄相同时按姓名降序
return -this.name.compareTo(student.name);
}
throw new RuntimeException("输入的类型不匹配");
}
}
import java.util.TreeSet;
public class SetDemo {
public static void main(String[] args) {
Student student = new Student(1, "王五", 20);
Student student02 = new Student(2, "张三", 30);
Student student03 = new Student(3, "小花", 33);
Student student04 = new Student(4, "笑笑", 24);
TreeSet
treeSet.add(student);
treeSet.add(student02);
treeSet.add(student03);
treeSet.add(student04);
System.out.println(treeSet);
}
}
程序运行结果为:
[Student{id=1, name='王五', age=20}, Student{id=4, name='笑笑', age=24}, Student{id=2, name='张三', age=30}, Student{id=3, name='小花', age=33}]
在上述代码中,把一个对象添加到 treeSet 集合中时,该对象的类必须实现 Comparable 接口,实现 Comparable 接口的类必须实现 compareTo() 方法,两个对象通过 compareTo() 方法的返回值来比较大小。
2) 定制排序
如果对象所属的类没有实现 Comparable 接口,或不希望按照升序(默认情况)排列,就可以考虑使用定制排序。定制排序通过 Comparator 接口实现,需要重写 compare(Object o1, Object o2) 方法。
【实例】创建集合和 Student 类,按照对象中的年龄从小到大进行排序,采用定制排序方法,代码如下:
import java.util.Objects;
public class Student02 {
private Integer id;
private String name;
private Integer age;
public Student02(Integer id, String name, Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
public Student02() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student02 student = (Student02) o;
return Objects.equals(id, student.id) &&
Objects.equals(name, student.name) &&
Objects.equals(age, student.age);
}
@Override
public int hashCode() {
return Objects.hash(id, name, age);
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
import java.util.Comparator;
import java.util.TreeSet;
public class SetDemo04 {
public static void main(String[] args) {
Student02 student = new Student02(1, "王五", 20);
Student02 student02 = new Student02(2, "张三", 30);
Student02 student04 = new Student02(4, "笑笑", 24);
Student02 student03 = new Student02(3, "小花", 33);
Comparator
@Override
public int compare(Student02 o1, Student02 o2) {
if (o1 instanceof Student02 && o2 instanceof Student02) {
Student02 s1 = (Student02) o1;
Student02 s2 = (Student02) o2;
int value = s1.getAge() - s2.getAge();
if (value != 0) {
return value;
}
return s1.getName().compareTo(s2.getName());
}
throw new RuntimeException("输入的类型不匹配");
}
};
TreeSet
treeSet.add(student);
treeSet.add(student02);
treeSet.add(student03);
treeSet.add(student04);
System.out.println(treeSet);
}
}
程序运行结果为:
[Student{id=1, name='王五', age=20}, Student{id=4, name='笑笑', age=24}, Student{id=2, name='张三', age=30}, Student{id=3, name='小花', age=33}]
在上述代码中,利用 compare(Object o1, Object o2) 方法比较 o1 和 o2 的大小:若返回正整数,则表示 o1 大于 o2;若返回 0,则表示两者相等;若返回负整数,则表示 o1 小于 o2。要实现定制排序,需要将实现 Comparator 接口的实例作为形式参数传递给 TreeSet 的构造器。