Java Set集合类的用法(非常详细)
Set 是 Collection 接口的子接口,Set 接口没有提供额外的方法。
Set 类型的集合不允许包含相同的元素,若把两个相同的元素加入同一个 Set 类型的集合中,则添加操作会失败。
Set 类型的集合支持的遍历方式和 Collection 类型的集合一样,可以用 foreach 和 iterator。Set 接口的常用实现类有 HashSet、TreeSet。
HashSet 用哈希算法存储集合中的元素,因此具有很好的存储、查找、删除等性能。HashSet 不能保证元素的排列顺序,不是线程安全的,集合元素可以是 null。
【实例】创建集合,使用 HashSet 实现集合中基本的功能,代码如下:
判断集合中两个元素相等的标准:两个对象通过 hashCode() 方法得到的哈希值相等,且两个对象的 equals() 方法返回值为 true。
对存放在 Set 类型的集合中的对象,对应的类一定要重写 hashCode() 和 equals() 方法,以符合对象相等规则,即“相等的对象必须具有相等的哈希值”。
HashSet 类型的集合中元素的无序性不等同于随机性,这里的无序性与元素的添加位置有关。具体来说,我们在添加每个元素到数组中时,具体的存储位置都是由元素的 hashCode() 调用后返回的哈希值决定的,导致在数组中的每个元素不是依次紧密存放的,表现出一定的无序性。
【实例】创建集合和 Student 类,在 HashSet 类型的集合中判断两个对象是否相等,代码如下:
TreeSet 有两种排序方法,分别是自然排序和定制排序。在默认情况下,TreeSet 采用自然排序。
【实例】创建集合和 Student 类,按照对象的年龄从小到大进行排序,采用自然排序方法,代码如下:
【实例】创建集合和 Student 类,按照对象中的年龄从小到大进行排序,采用定制排序方法,代码如下:
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<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 + '}'; } @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<Student> treeSet = new 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<Student02> comparator = new Comparator<Student02>() { @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<Student02> treeSet = new TreeSet<>(comparator); 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 的构造器。