1. 基本概念
- Set 接口也属于 Collection 的常用接口,其最大特点是不允许出现重复数据。
- Set 接口的定义
(1) Set 接口也是 Collection 的子接口,定义:public interface Set<E> extends Collection<E>
。
(2)Set 接口主要方法与 Collection 是一致的,也就是说 Set 接口并没有对 Collection 接口进行扩充,只是比 Collection 接口的要求更加严格,不能增加重复的元素。
(3)Set 接口的实例无法像 List 接口那样可以进行双向输出,因为此接口没有提供像 List 接口定义的 get(int index ) 方法。
2. Set 接口的常用子类
Set 接口中有HashSet(散列存放) 和 TreeSet( 有序存放)两个常用子类,这两个子类在使用时都会有自己的实现要求。
2.1 散列存放:HashSet
HashSet 是 Set 的一个子类,主要特点是:里面不能存放重复的元素,而且是采用散列的存储方式,所以没有顺序。
实例1代码:
package self.learn.setdemo;
import java.util.HashSet;
import java.util.Set;
public class SetDemo {
public static void main(String[] args) {
Set<String> allSet = new HashSet<String>();
allSet.add("A"); // 增加元素
allSet.add("B");
allSet.add("C");
allSet.add("C"); // 重复元素,不能加人
allSet.add("C"); // 重复元素,不能加人
allSet.add("D");
System.out.println(allSet); // 输出集合对象,调用 toString()方法
}
}
运行结果截图:
从结果中可以看出,对于重复的元素只会增加一次,而且程序运行时向集合中加入元素的顺序并不是集合中的保存顺序,证明 HashSet 类中的元素是无序存放的。
2.2 有序的存放:TreeSet
如果想对输入的数据进行有序排列,则要使用 TreeSet 子类。TreeSet 类定义如下:
public class TreeSet<E> extends AbstractSet<E>
implements Sorted<E>,Cloneable,Serializable
TreeSet 类 也是继承了 AbstractSet 类,此类定义如下:
public abstract class AbstractSet<E> extends AbstractCollection<E>
implements Set<E>
实例2代码:
package self.learn.setdemo;
import java.util.Set;
import java.util.TreeSet;
public class SetDemo {
public static void main(String[] args) {
Set<String> allSet = new TreeSet<String>();
allSet.add("D");
allSet.add("C");
allSet.add("C"); // 重复元素,不能加人
allSet.add("C"); // 重复元素,不能加人
allSet.add("A"); // 增加元素
allSet.add("B");
System.out.println(allSet); // 输出集合对象,调用 toString()方法
}
}
运行结果截图:
2.3 关于 TreeSet 的排序说明:自定义类排序
自定义 Person 类
package self.learn.setdemo;
public class Person {
private String name;
private int age;
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
package self.learn.setdemo;
import java.util.Set;
import java.util.TreeSet;
public class SetDemo {
public static void main(String[] args) {
Set<Person> allSet = new TreeSet<Person>(); // 实例 Set 集合类
allSet.add(new Person("张三", 30));
allSet.add(new Person("李四", 31));
allSet.add(new Person("王五", 32)); // 重复元素,不能加人
allSet.add(new Person("王五", 32)); // 重复元素,不能加人
allSet.add(new Person("王五", 32)); // 重复元素,不能加人
allSet.add(new Person("赵六", 33));
allSet.add(new Person("孙七", 33)); // 年龄重复
System.out.println(allSet); // 输出集合对象,调用 toString()方法
}
}
运行结果截图:
由图可知,出现了类转换异常,存现这样的问题是因为 TreeSet 中的元素是有序存放的,所以对于一个对象必须指定好其排序规则,且 TreeSet 中的每个对象所在类都必须实现 Comparable 接口才可以正常使用。
person 类指定好排序规则后
package self.learn.setdemo;
public class Person implements Comparable<Person>{ // 定义 Person 类实现比较器
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() { // 覆写 toString() 方法
return "Person [name=" + name + ", age=" + age + "]";
}
public int compareTo(Person per) { // 覆写 compareTo() 方法,指定排序规则
if(this.age > per.age) { // 通过年龄排序
return 1;
}else if (this.age < per.age) {
return -1;
}else {
return 0;
}
}
}
package self.learn.setdemo;
import java.util.Set;
import java.util.TreeSet;
public class SetDemo {
public static void main(String[] args) {
Set<Person> allSet = new TreeSet<Person>(); // 实例 Set 集合类
allSet.add(new Person("张三", 30));
allSet.add(new Person("李四", 31));
allSet.add(new Person("王五", 32));
allSet.add(new Person("王五", 32)); // 重复元素,不能加人
allSet.add(new Person("王五", 32)); // 重复元素,不能加人
allSet.add(new Person("赵六", 33));
allSet.add(new Person("孙七", 33)); // 年龄重复
System.out.println(allSet); // 输出集合对象,调用 toString()方法
}
}
运行结果截图:
从程序的运行结果 中可以发现,重复的“王五” 对象只有一个,“赵六”和“孙七” 的数据中姓名并不重复,只有年龄重复了,但是“孙七” 却没有加入到集合中。这是因为采用了比较器造成的,比较器操作的时候如果某个属性没有进行比较的话,则也会认为是同一个对象,所以此时应该将 Person 类中的 compareTo 方法中增加按姓名比较。
修改 person 的比较器后
package self.learn.setdemo;
public class Person implements Comparable<Person>{ // 定义 Person 类实现比较器
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() { // 覆写 toString() 方法
return "Person [name=" + name + ", age=" + age + "]";
}
public int compareTo(Person per) { // 覆写 compareTo() 方法,指定排序规则
if(this.age > per.age) { // 通过年龄排序
return 1;
}else if (this.age < per.age) {
return -1;
}else {
return this.name.compareTo(per.name); // 增加字符串的比较
}
}
}
package self.learn.setdemo;
import java.util.Set;
import java.util.TreeSet;
public class SetDemo {
public static void main(String[] args) {
Set<Person> allSet = new TreeSet<Person>(); // 实例 Set 集合类
allSet.add(new Person("张三", 30));
allSet.add(new Person("李四", 31));
allSet.add(new Person("王五", 32));
allSet.add(new Person("王五", 32)); // 重复元素,不能加人
allSet.add(new Person("王五", 32)); // 重复元素,不能加人
allSet.add(new Person("赵六", 33));
allSet.add(new Person("孙七", 33)); // 年龄重复
System.out.println(allSet); // 输出集合对象,调用 toString()方法
}
}
运行结果截图:
上面的程序运行结果中“孙七”出现了,而且去掉了重复的内容,但是此时的重复内容并不是真正意义的去掉。因为此时靠的是 Comparable 接口完成的,如果现在换成 HashSet 也会出现重复的内容。所以想要真正的重复内容去掉,必须深入研究 Object 类。
package self.learn.setdemo;
import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;
public class SetDemo {
public static void main(String[] args) {
Set<Person> allSet = new HashSet<Person>(); // 实例 Set 集合类
allSet.add(new Person("张三", 30));
allSet.add(new Person("李四", 31));
allSet.add(new Person("王五", 32));
allSet.add(new Person("王五", 32)); // 重复元素,不能加人
allSet.add(new Person("王五", 32)); // 重复元素,不能加人
allSet.add(new Person("赵六", 33));
allSet.add(new Person("孙七", 33)); // 年龄重复
System.out.println(allSet); // 输出集合对象,调用 toString()方法
}
}
运行结果截图:
从程序的运行结果可以发现,“王五”的内容重复了,也就是说,此时的程序并没有像 Set 接口规定的那样是不允许出现重发元素的。如果此时想要去掉重复的元素,则必须首先进行对象是否重复的判断,要想进行这样的判断则一个类中就必须覆写 Object 类中的 equals()方法,才能完成对象是否相等的判断,但是只覆写 equals()方法是不够的,还需覆写 hashCode() 方法,此方法表示一个哈希码,可以将类中的全部属性进行适当的计算,以求出一个不会重复的哈希码。
通过 Object 类去掉重复元素,通过实现 Comparable 接口排序
package self.learn.setdemo;
public class Person implements Comparable<Person> { // 定义 Person 类实现比较器
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public boolean equals(Object obj) { // 覆写 equals() 方法
if(this == obj) { // 地址相等
return true; // 是同一对象
}
if(!(obj instanceof Person)) { // 传递进来的不是本类对象
return false; // 不是同一对象
}
Person p = (Person)obj; // 进行向下转型
if(this.name.equals(p.name)&&this.age == p.age) { // 属性依次比较
return true; // 全部属性相等,是同一对象
}else {
return true; // 属性不相等,不是同一对象
}
}
public int hashCode() { // 覆写 hashCode() 方法
return this.name.hashCode()*this.age; // 指定编码公式
}
public int compareTo(Person per) { // 覆写 compareTo() 方法,指定排序规则
if(this.age > per.age) { // 通过年龄排序
return 1;
}else if (this.age < per.age) {
return -1;
}else {
return this.name.compareTo(per.name); // 增加字符串的比较
}
}
@Override
public String toString() { // 覆写 toString() 方法
return "Person [name=" + name + ", age=" + age + "]";
}
}
package self.learn.setdemo;
import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;
public class SetDemo {
public static void main(String[] args) {
//Set<Person> allSet = new TreeSet<Person>(); // 实例 Set 集合类
Set<Person> allSet = new HashSet<Person>();
allSet.add(new Person("张三", 30));
allSet.add(new Person("李四", 31));
allSet.add(new Person("王五", 32));
allSet.add(new Person("王五", 32)); // 重复元素,不能加人
allSet.add(new Person("王五", 32)); // 重复元素,不能加人
allSet.add(new Person("赵六", 33));
allSet.add(new Person("孙七", 33)); // 年龄重复
System.out.println(allSet); // 输出集合对象,调用 toString()方法
}
}
HashSet 子类运行结果截图:没有重复元素
TreeSet 子类运行结果截图:没有重复元素 及排序成功
提示: Object 类总结
经过以上的程序代码练习,Object 类中的全部方法就介绍完了,相信读者已经清楚地知道各个方法的作用,在实际的开发中经常会碰到区分同一对象的问题,所以,一个完整的类最好覆写 Object 类的 hashCode()、equals()、toString() 3 个方法。
3. SortedSet 接口
从 TreeSet 类的定义中可以发现,TreeSet 里实现了 SortedSet 接口,此接口主要是用于排序操作的,即实现此接口的子类都属于排序的子类。SortedSet 接口定义如下:
public interface SortedSet<E> extends Set<E>
SortedSet 接口中定义的方法如下表:
序号 | 方法 | 类型 | 描述 |
---|---|---|---|
1 | public Comparator<? super E> comparator() | 普通 | 返回与排序有关联的比较器 |
2 | public E first() | 普通 | 返回集合中的第一个元素 |
3 | public SortedSet<E> headSet(E toElement) | 普通 | 返回从开始到指定的元素的集合 |
4 | public E last() | 普通 | 返回最后一个元素 |
5 | public SortedSet<E> subSet(E fromElement,E toElement) | 普通 | 返回指定对象间的元素 |
6 | public SortedSet<E> tailSet(E fromElement) | 普通 | 从指定元素到最后 |
3.1 验证 SortedSet 接口
package self.learn.setdemo;
import java.util.SortedSet;
import java.util.TreeSet;
public class SetDemo {
public static void main(String[] args) {
SortedSet<String> allSet = new TreeSet<String>(); // 实例 Set 集合类
allSet.add("A");
allSet.add("B");
allSet.add("C");
allSet.add("C"); // 重复元素,不能加入
allSet.add("C"); // 重复元素,不能加入
allSet.add("D");
allSet.add("E"); // 年龄重复
System.out.println("第一个元素:"+allSet.first()); // 输出集合对象,调用 toString()方法
System.out.println("第一个元素:"+allSet.last());
System.out.println("headSet 元素:"+allSet.headSet("C"));
System.out.println("tailSet 元素:"+allSet.tailSet("C"));
System.out.println("subSet 元素:"+allSet.subSet("A","D"));
}
}