@toc
1、Set集合
Set接口也是Collection的子接口,Set接口没有提供额外的方法。Set集合支持的遍历方式也和Collection集合一样,使用foreach和Iterator遍历。
==Set集合不允许包含相同的元素==,如果试图把两个相同的元素加入同一个Set集合,则添加操作失败,操作失败并不会报错,只是添加不成功而已。Set接口的常用实现类有:HashSet、LinkedHashSet、TreeSet。
1.1 HashSet和LinkedHashSet
HashSet是Set接口的典型实现类,大多数时候使用Set集合时都会使用这个实现类。
HashSet和LinkedHashSet按Hash算法来存储集合中的元素,因此具有很好的存取和查找性能。==HashSet和LinkedHashSet集合判断两个元素相等的标准是两个对象通过hashCode方法比较,并且两个对象的equals方法返回值也相等。==
LinkedHashSet是HashSet的子类,它在HashSet的基础上,在结点中增加两个属性(before和after)以维护结点的前后添加顺序。LinkedHashSet插入性能略低于HashSet,但在迭代访问Set中的全部元素时有很好的性能。
示例代码:
HashSet添加元素的示例代码:
public class HashSetTest {
public static void main(String[] args) {
HashSet<String> set = new HashSet<>();
set.add("张三");
set.add("李四");
set.add("王五");
set.add("张三");//尝试添加重复元素
System.out.println("元素个数:"+set.size());
for (Object obj : set) {
System.out.println(obj);
}
}
}
LinkedHashSet添加元素的示例代码:
public class LinkedHashSetTest {
public static void main(String[] args) {
LinkedHashSet<String> set = new LinkedHashSet<>();
set.add("张三");
set.add("李四");
set.add("王五");
set.add("张三");//尝试添加重复元素
System.out.println("元素个数:"+set.size());
for (Object obj : set) {
System.out.println(obj);
}
}
}
从上面代码的结果中可以看出,HashSet不保证元素的添加顺序,但是LinkedHashSet可以保证元素的添加顺序。无论是HashSet还是LinkedHashSet都不允许添加重复元素。
1.2 案例:员工信息管理
案例需求:定义一个Employee类,该类包含name、birthday,要求name和恶birthday相等的为同一个员工,其中birthday为MyDate类,包含年、月、日三个属性。尝试重写Employee类的hashCode方法和equals方法。
MyDate类代码:
import java.util.Objects;
public class MyDate {
private int year;
private int month;
private int day;
public MyDate(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
@Override
public String toString() {
return "MyDate{" +
"year=" + year +
", month=" + month +
", day=" + day +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MyDate myDate = (MyDate) o;
return year == myDate.year &&
month == myDate.month &&
day == myDate.day;
}
@Override
public int hashCode() {
return Objects.hash(year, month, day);
}
}
Employee类示例代码:
import java.util.Objects;
public class Employee {
private String name;//姓名
private MyDate birthday;//生日
public Employee(String name, MyDate birthday) {
this.name = name;
this.birthday = birthday;
}
//重写equals方法-判断姓名和生日是否相等
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Employee employee = (Employee) o;
return Objects.equals(name, employee.name) &&
Objects.equals(birthday, employee.birthday);
}
@Override
public int hashCode() {
return Objects.hash(name, birthday);
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", birthday=" + birthday +
'}';
}
}
测试类示例代码:
import java.util.HashSet;
public class TestEmployee {
public static void main(String[] args) {
HashSet<Object> set = new HashSet<>();
set.add(new Employee("张三",new MyDate(1990,1,1)));
//重复元素无法添加,因为MyDate和Employee重写了hashCode和equals方法
set.add(new Employee("张三",new MyDate(1990,1,1)));
set.add(new Employee("李四",new MyDate(1992,2,2)));
for (Object o : set) {
System.out.println(o);
}
}
}
代码运行结果:
1.3 TreeSet
SortedSet是Set接口的一个子接口,支持排序类Set集合,TreeSet是SortedSet接口的实现类,即TreeSet可以确保集合元素处于排序状态。
对象的排序要么是对象本身支持自然排序,即实现java.lang.Compareable
接口,要么在创建set集合对象时提供定值排序接口java.util.Comparator
的实现类对象。
1.3.1 自然排序
如果视图把一个对象添加到未指定定制比较器的TreeSet时,则该对象的类必须实现Comparable接口,实现compareTo(Object obj)
方法。此时对于TreeSet集合而言,它判断两个对象是否相等的唯一标准是两个对象通过compareTo(Object obj)
方法比较返回值为0.
示例代码:
元素为String类,String类实现了java.lang.Conparable
自然排序接口的示例代码:
import java.util.TreeSet;
public class TreeSetTest1 {
public static void main(String[] args) {
TreeSet<Object> set = new TreeSet<>();
//String它实现了java.lang.Comparable接口
set.add("zhangsan");
set.add("lisi");
set.add("wangwu");
set.add("zhangsan");
System.out.println("元素个数:"+set.size());
for (Object o : set) {
System.out.println(o);
}
}
}
学生类实现了java.lang.Comparable
自然排序接口的示例代码:
public class Student implements Comparable {
private int id;
private String name;
public Student(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int compareTo(Object o) {
Student other=(Student) o;
return this.id-other.id;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
测试类示例代码:
public class TreeSetTest2 {
public static void main(String[] args) {
TreeSet<Object> set = new TreeSet<>();
//Student实现了java.lang.Comparable接口
set.add(new Student(3,"张三"));
set.add(new Student(1,"李四"));
set.add(new Student(2,"王五"));
set.add(new Student(3,"张三凤"));
System.out.println("元素个数:"+set.size());
for (Object o : set) {
System.out.println(o);
}
}
}
虽然添加到TreeSet时,不使用equals方法,但在Comparable接口的API中有如下提醒:==当元素实现java.lang.Comparable
接口重写compareTo
方法时,也建议重写equals
方法,应保证该方法与compareTo(Object obj)
方法有一致的结果,如果两个对象通过equals
方法比较返回true,则通过compareTo(Object obj)
方法比较的返回值为0,否则结果会有点奇怪。==
1.3.2 定制排序
如果放到TreeSet中的元素的自然排序规则不符合当前业务的排序需求,或者元素的类型没有实现Comparable接口,那么在创建TreeSet时,可以单独指定一个定制比较器Comparator
的实现类对象。使用定制比较器的TreeSet判断两个元素相等的标准是通过compare(Object o1,Object o2)
方法比较两个元素返回了0。
示例代码:
Teacher类示例代码:
public class Teacher {
private int id;
private String name;
public Teacher(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Teacher{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
测试类示例代码:
import java.util.Comparator;
import java.util.TreeSet;
public class TreeSetTest3 {
public static void main(String[] args) {
TreeSet<Object> set = new TreeSet<>(new Comparator<Object>() {
@Override
public int compare(Object o1, Object o2) {
Teacher t1 = (Teacher) o1;
Teacher t2 = (Teacher) o2;
return t1.getId() - t2.getId();
}
});
set.add(new Teacher(3,"张三"));
set.add(new Teacher(1,"李四"));
set.add(new Teacher(2,"王五"));
set.add(new Teacher(3,"张三凤"));
System.out.println("元素个数:"+set.size());
for (Object o : set) {
System.out.println(o);
}
}
}
1.4 案例:企业面试题
案例需求:通过键盘录入一串字符,去掉其中的重复字符,打印出不同的字符(重复的字符仅保留一份),必须保证输入顺序。例如,输入baaabbccacddd,打印结果为bacd。
示例代码:
import java.util.LinkedHashSet;
import java.util.Scanner;
public class SetExer {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.println("请输入一串字符:");
String strings = input.next();
LinkedHashSet<Object> set = new LinkedHashSet<>();
char[] chars = strings.toCharArray();
for (int i = 0; i < chars.length; i++) {
set.add(chars[i]);
}
String result="";
for (Object o : set) {
result+=o;
}
System.out.println("result="+result);
}
}