Set集合
Set集合是一个不允许有重复元素的Collection集合,元素无序且唯一,它的主要实现类有HashSet,LinkedHashSet,TreeSet
Set三个子类的特点
HashSet:底层数据结构是哈希表(数组+链表),元素无序(存取顺序不一致),且唯一,集合元素可以是null
LinkedHashSet:底层数据结构是链表+哈希表,元素有序且唯一
TreeSet:元素唯一,且可以对元素进行排序
HashSet
为什么说HashSet是无序且唯一的?
HashSet在存储元素时,HashSet会调用对象的hashCode()方法,得到对象的hashCode值,然后根据 hashCode 值决定该对象在 HashSet 中的存储位置。
HashSet 集合判断两个元素相等的标准:
两个对象通过 hashCode() 方法比较相等,并且两个对象的 equals() 方法返回值也相等。
@Override
public boolean equals(Object obj) {
// System.out.println(this + "---" + obj);
if (this == obj) {
return true;
}
if (!(obj instanceof Student)) {
return false;
}
Student s = (Student) obj;
return this.name.equals(s.name) && this.age == s.age;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
存储自定义类型的数据时,要重写equals()和hashcode()方法
以学生类型为例:
Student s1 = new Student("张双衡", 19);
Student s2 = new Student("王伟腾", 19);
Student s3 = new Student("陈佳坤", 19);
Student s4 = new Student("刘鹏", 20);
Student s5 = new Student("项保", 23);
Student s6 = new Student("王伟腾", 19);
Student s7 = new Student("张双衡", 19);
HashSet<Student> students = new HashSet<>();
students.add(s1);
students.add(s2);
students.add(s3);
students.add(s4);
students.add(s5);
students.add(s6);
students.add(s7);
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", 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 age == student.age &&
Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
LinkedHashSet
LinkedHashSet 底层数据结构是链表和哈希表 元素有序且唯一 链表保证了有序,哈希表保证了唯一,线程不安全效率高
LinkedHashSet<String> strings = new LinkedHashSet<>();
strings.add("aaa");
strings.add("bbb");
strings.add("ccc");
strings.add("ddd");
strings.add("eee");
strings.add("fff");
strings.add("aaa");
strings.add("bbb");
strings.add("ccc");
strings.add("ddd");
strings.add("eee");
strings.add("fff");
for (String string : strings) {
System.out.println(string);
}
当添加相同元素时,重复安苏和i能添加一次。我们可以利用他去除重复元素
ArrayList<String> strings1 = new ArrayList<>();
strings1.add("aaaaa");
strings1.add("bbbbb");
strings1.add("ccc");
strings1.add("dddd");
strings1.add("eeee");
strings1.add("aaaaa");
strings1.add("bbbbb");
strings1.add("ccc");
strings1.add("dddd");
strings1.add("eeee");
HashSet<String> strings2 = new HashSet<>(strings1);
System.out.println(strings2);
LinkedHashSet<String> strings3 = new LinkedHashSet<>(strings1);
System.out.println(strings3);
使用HashSet和LinkedHashSet都可做到元素去重,但是 HashSet打印元素是无序的,LinkedHashSet就可以保证元素有序性。
TreeSet
TreeSet 元素唯一,且可以对元素进行排序
TreeSet<Integer> treeSet = new TreeSet<>();
treeSet.add(20);
treeSet.add(18);
treeSet.add(23);
treeSet.add(22);
treeSet.add(17);
treeSet.add(24);
treeSet.add(19);
treeSet.add(18);
treeSet.add(24);
System.out.println(treeSet);
运行结果:
TreeSet存储引用类型时可以进行排序,若是存储自定义类型,是否还能排序呢
当我们向集合中添加自定义类型的是数据时,程序及会出错,之所以存储引用类型不会报错,是因为java已经对其进行了排序
自然排序和比较器排序
TreeSet有两种排序方式,自然排序和比较器排序,
自然排序:如果采用的是空参构造,那么采用的就是自然排序,如果是自然排序,那么对元素有要求,要求元素必须实现一个Comparable接口,重写这个接口中的一个compareTo这个比较的方法,根据此方法的返回值的正 负 0 来决定元素,排列的位置
我们以学生类型为例
public class Student implements Comparable<Student> {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
//这是一个比较的方法
@Override
public int compareTo(Student student) {
//根据学生的年龄大小来排序
//年龄一样,并不能说明是同一个对象,我们还得比较姓名是否一样
int num=this.age-student.age;
int num2=num==0?this.name.compareTo(student.name):num;
return -num2; //根据返回值的正 负 0 来决定元素的放置位置
}
}
**比较器排序:**采用有参构造,你在创建TreeSet对象时,需要传入一个Comparetor 比较器
TreeSet<Student> treeSet = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
//根据姓名长度来排序
int num = s1.getName().length() - s2.getName().length();
int num2=num==0?s1.getName().compareTo(s2.getName()):num;
int num3=num2==0?s1.getAge()-s2.getAge():num2;
return num3;
}
});
treeSet.add(new Student("张三士大夫士大夫", 23));
treeSet.add(new Student("张三是非得失", 23));
treeSet.add(new Student("李四大幅度发", 23));
treeSet.add(new Student("王五大幅度发", 20));
treeSet.add(new Student("周六的", 30));
treeSet.add(new Student("田七大幅度", 23));
treeSet.add(new Student("李白大幅度发", 33));
treeSet.add(new Student("刘星大幅度发", 63));
treeSet.add(new Student("夏雨", 78));
treeSet.add(new Student("张子豪", 53));
for (Student student : treeSet) {
System.out.println(student);
}