Java Set:HashSet和TreeSet类
Set 实现了 Collection 接口,它主要有两个常用的实现类:HashSet 类和 TreeSet类。
HashSet 类
HashSet 是 Set 接口的典型实现,大多数时候使用 Set 时就是使用这个实现类。HashSet 是按照 Hash 算法来存储中的元素。因此具有很好的存取和查找性能。HashSet 具有以下特点:
不能保证元素的排列顺序,顺序可能与添加顺序不同,顺序也有可能发生变化。
HashSet 不是同步的,如果多个线程同时访问或修改一个 HashSet,则必须通过代码来保证其同步。
元素值可以是 null。
当向 HashSet 中存入一个元素时,HashSet 会调用该对象的 hashCode() 方法来得到该对象的 hashCode 值,然后根据该 hashCode 值决定该对象在 HashSet 中的存储位置。如果有两个元素通过 equals() 方法比较返回的结果为 true,但它们的 hashCode 不相等,HashSet 将会把它们存储在不同的位置,依然可以添加成功。
也就是说,两个对象的 hashCode 值相等且通过 equals() 方法比较返回结果为 true,则 HashSet 认为两个元素相等。
在 HashSet 类中实现了 Collection 接口中的所有方法。HashSet 类的常用构造方法重载形式如下。
HashSet():构造一个新的空的 Set 。
HashSet(Collection<? extends E>c):构造一个包含指定 Collection 元素的新 Set 。其中,“< >”中的 extends 表示 HashSet 的父类,即指明该 Set 中存放的元素类型。c 表示其中的元素将被存放在此 Set 中。
下面的代码演示了创建两种不同形式的 HashSet 对象。
HashSet hs = new HashSet(); // 调用无参的构造函数创建HashSet对象
HashSet<String> hss = new HashSet<String>(); // 创建泛型的 HashSet 对象
例 1
编写一个 Java 程序,使用 HashSet 创建一个 Set ,并向该中添加 4 套教程。具体实现代码如下:
如上述代码,首先使用 HashSet 类的构造方法创建了一个 Set ,接着创建了 4 个 String 类型的对象,并将这些对象存储到 Set 中。使用 HashSet 类中的 iterator() 方法获取一个 Iterator 对象,并调用其 hasNext() 方法遍历元素,再将使用 next() 方法读取的元素强制转换为 String 类型。调用 HashSet 类中的 size() 方法获取元素个数。public static void main(String[] args) {
HashSet<String> courseSet = new HashSet<String>(); // 创建一个空的 Set
String course1 = new String("Java入门教程");
String course2 = new String("Python基础教程");
String course3 = new String("C语言学习教程");
String course4 = new String("Golang入门教程");
courseSet.add(course1); // 将 course1 存储到 Set 中
courseSet.add(course2); // 将 course2 存储到 Set 中
courseSet.add(course3); // 将 course3 存储到 Set 中
courseSet.add(course4); // 将 course4 存储到 Set 中
System.out.println("C语言中文网教程有:");
Iterator<String> it = courseSet.iterator();
while (it.hasNext()) {
System.out.println("《" + (String) it.next() + "》"); // 输出 Set 中的元素
}
System.out.println("有" + courseSet.size() + "套精彩教程!");
}
运行该程序,输出的结果如下:
注意:在以上示例中,如果再向 CourseSet 中再添加一个名称为“Java入门教程”的 String 对象,则输出的结果与上述执行结果相同。也就是说,如果向 Set 中添加两个相同的元素,则后添加的会覆盖前面添加的元素,即在 Set 中不会出现相同的元素。C语言中文网教程有:
《Java入门教程》
《C语言学习教程》
《Python基础教程》
《Golang入门教程》
有4套精彩教程!
TreeSet 类
TreeSet 类同时实现了 Set 接口和 SortedSet 接口。SortedSet 接口是 Set 接口的子接口,可以实现对进行自然排序,因此使用 TreeSet 类实现的 Set 接口默认情况下是自然排序的,这里的自然排序指的是升序排序。TreeSet 只能对实现了 Comparable 接口的类对象进行排序,因为 Comparable 接口中有一个 compareTo(Object o) 方法用于比较两个对象的大小。例如 a.compareTo(b),如果 a 和 b 相等,则该方法返回 0;如果 a 大于 b,则该方法返回大于 0 的值;如果 a 小于 b,则该方法返回小于 0 的值。
表 1 列举了 JDK 类库中实现 Comparable 接口的类,以及这些类对象的比较方式。
类 | 比较方式 | |
---|---|---|
包装类(BigDecimal、Biglnteger、 Byte、Double、 Float、Integer、Long 及 Short) | 按数字大小比较 | |
Character | 按字符的 Unicode 值的数字大小比较 | |
String | 按字符串中字符的 Unicode 值的数字大小比较 |
TreeSet 类除了实现 Collection 接口的所有方法之外,还提供了如表 2 所示的方法。
方法名称 | 说明 |
---|---|
E first() | 返回此中的个元素。其中,E 表示中元素的数据类型 |
E last() | 返回此中的一个元素 |
E poolFirst() | 获取并移除此中的个元素 |
E poolLast() | 获取并移除此中的一个元素 |
SortedSet<E> subSet(E fromElement,E toElement) | 返回一个新的,新包含原中 fromElement 对象与 toElement 对象之间的所有对象。包含 fromElement 对象,不包含 toElement 对象 |
SortedSet<E> headSet<E toElement〉 | 返回一个新的,新包含原中 toElement 对象之前的所有对象。 不包含 toElement 对象 |
SortedSet<E> tailSet(E fromElement) | 返回一个新的,新包含原中 fromElement 对象之后的所有对 象。包含 fromElement 对象 |
注意:表面上看起来这些方法很多,其实很简单。因为 TreeSet 中的元素是有序的,所以增加了访问个、前一个、后一个、一个元素的方法,并提供了 3 个从 TreeSet 中截取子 TreeSet 的方法。
例 2
本次有 5 名学生参加考试,当老师录入每名学生的成绩后,程序将按照从低到高的排列顺序显示学生成绩。此外,老师可以查询本次考试是否有满分的学生存在,不及格的成绩有哪些,90 分以上成绩的学生有几名。下面使用 TreeSet 类来创建 Set ,完成学生成绩查询功能。具体的代码如下:
如上述代码,首先创建一个 TreeSet 对象 scores,并向该中添加 5 个 Double 对象。接着使用 while 循环遍历 scores 对象,输出该对象中的元素,然后调用 TreeSet 类中的 contains() 方法获取该中是否存在指定的元素。分别调用 TreeSet 类中的 headSet() 方法和 tailSet() 方法获取不及格的成绩和 90 分以上的成绩。public class Test08 {
public static void main(String[] args) {
TreeSet<Double> scores = new TreeSet<Double>(); // 创建 TreeSet
Scanner input = new Scanner(System.in);
System.out.println("------------学生成绩管理系统-------------");
for (int i = 0; i < 5; i++) {
System.out.println("第" + (i + 1) + "个学生成绩:");
double score = input.nextDouble();
// 将学生成绩转换为Double类型,添加到TreeSet中
scores.add(Double.valueOf(score));
}
Iterator<Double> it = scores.iterator(); // 创建 Iterator 对象
System.out.println("学生成绩从低到高的排序为:");
while (it.hasNext()) {
System.out.print(it.next() + "\t");
}
System.out.println("\n请输入要查询的成绩:");
double searchScore = input.nextDouble();
if (scores.contains(searchScore)) {
System.out.println("成绩为: " + searchScore + " 的学生存在!");
} else {
System.out.println("成绩为: " + searchScore + " 的学生不存在!");
}
// 查询不及格的学生成绩
SortedSet<Double> score1 = scores.headSet(60.0);
System.out.println("\n不及格的成绩有:");
for (int i = 0; i < score1.toArray().length; i++) {
System.out.print(score1.toArray()[i] + "\t");
}
// 查询90分以上的学生成绩
SortedSet<Double> score2 = scores.tailSet(90.0);
System.out.println("\n90 分以上的成绩有:");
for (int i = 0; i < score2.toArray().length; i++) {
System.out.print(score2.toArray()[i] + "\t");
}
}
}
运行该程序,执行结果如下所示。
注意:在使用自然排序时只能向 TreeSet 中添加相同数据类型的对象,否则会抛出 ClassCastException 异常。如果向 TreeSet 中添加了一个 Double 类型的对象,则后面只能添加 Double 对象,不能再添加其他类型的对象,例如 String 对象等。------------学生成绩管理系统-------------
第1个学生成绩:
53
第2个学生成绩:
48
第3个学生成绩:
85
第4个学生成绩:
98
第5个学生成绩:
68
学生成绩从低到高的排序为:
48.0 53.0 68.0 85.0 98.0
请输入要查询的成绩:
90
成绩为: 90.0 的学生不存在!
不及格的成绩有:
48.0 53.0
90 分以上的成绩有:
98.0