文章目录
Set概述及其使用方法
set集合的特点是:无序(自己有顺序,但是不一定和输入的顺序一致),不重复。
自创建学生类对象
- 如果只是简单的实现comparable接口,那么可以不需要写get以及set方法,如果想通过comparator自定义排序,那么就需要通过get与set方法获得其值,从而进行比较。
package com.wenhua.project;
import java.util.Objects;
/**
* 自定义学生类,实现在hashSet和treeSet的存储
*/
public class Student implements Comparable<Student> {
// 学生类中的属性
private String name;
private Integer age;
// 带参构造方法,必须赋值
public Student(String name, Integer age) {
this.name = name;
this.age = age;
}
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;
}
// 存储在HashSet中的类型,通过hashCode先判断,如果hash相等,则通过equals方法进行比较
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return name.equals(student.name) &&
age.equals(student.age);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
// toString方法可以理解为一种视图,让人们更直观
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", grade=" + age +
'}';
}
// 存储在treeSet中时,就必须实现Comparable接口,重写compareTo方法
@Override
public int compareTo(Student o) {
// return this.name.compareTo(o.name); 比较字符串的大小
return this.age - o.age;// 当判断集合中已经有了,那么不会覆盖只会丢弃,保留原来的值
}
}
HashSet实现存储自定义的引用对象
HashSet底层是数组+链表+红黑树,需要重写hashCode方法和equals方法
- 其实hashSet实现存储是通过hash值确定数组的位置,(如果hash值相同,那么通过equals方法比较其值),判断是否重复,如果重复,则抛弃,从而确定其存储的链表位置。
public static void main(String[] args) {
Set<Student> set = new HashSet<Student>();
Student student = new Student("Java",20);
Student student1 = new Student("jdk",21);
Student student2 = new Student("jvm",22);
Student student3 = new Student("Java",20);
Student student4 = new Student("jre",23);
// Ctrl + D 直接赋值一行
set.add(student);
set.add(student1);
set.add(student2);
set.add(student3);
set.add(student4);
System.out.println(set);
}
运行结果
[Student{name='jdk', grade=21}, Student{name='jvm', grade=22}, Student{name='jre', grade=23}, Student{name='Java', grade=20}]
HashSet遇到的哈希冲突
字符冲突
public static void main(String[] args) {
Set<Character> set = new HashSet<Character>();
set.add('q');
set.add('a');// q(113) a(97) 的哈希位置一样(哈希冲突)
set.add('w');
set.add('d');
// 读取时按照哈希存储的位置前后进行读取(同一哈希位置按照链表顺序)
System.out.println(set);
}
运行结果
[q, a, d, w]
数字冲突
public static void main(String[] args) {
Set<Integer> set = new HashSet<Integer>();
set.add(11);
set.add(43);
set.add(27);// 11 43 27存储在同一个哈希位置
set.add(21);
set.add(5);// 21 5 存储在同一个哈希位置
set.add(10);
set.add(9);
set.add(87);
set.add(23);
// 读取时按照哈希表的存储顺序进行读取
for(Integer s :set) {
System.out.print(s+",");
}
}
运行结果
21,5,87,23,9,10,11,43,27,
TreeSet实现存储自定义的引用对象
TreeSet底层是数组+红黑树,需要排序
- 其实呀,在treeSet实现进行排序时,需要学生类实现Comparable接口,通过comparTo方法实现排序。在collections集合解释中有通过多个关键字进行排序处理。
public static void main(String[] args) {
Set<Student> set = new TreeSet<Student>();
Student student = new Student("Java",22);
Student student1 = new Student("JDK",21);
Student student2 = new Student("JRE",22);// 虽然age一样,但是名字不一样,所以是不同的对象需要存储
Student student3 = new Student("Java",20);
Student student4 = new Student("JVM",23);
Student student5 = new Student("JVM",23);// 同student4对象一模一样,所以就不会存储进去
// Ctrl + D 直接赋值一行
set.add(student);
set.add(student1);
set.add(student2);
set.add(student3);
set.add(student4);
set.add(student5);
// 需要存储的类型类,必须实现Comparable接口,实现排序。
// com.wenhua.project.Day1121.Student cannot be cast to java.lang.Comparable
System.out.println(set);
}
运行对象
[Student{name='Java', grade=20}, Student{name='JDK', grade=21}, Student{name='Java', grade=22}, Student{name='JVM', grade=23}]
通过CompareTo方法比较排序多个关键字
- 如果想要通过多个关键字进行排序,那么重写compareTo方法时,就需要按以下方式来写:
@Override
public int compareTo(Student o) {
// return this.name.compareTo(o.name); 比较字符串的大小
int num = this.age - o.age;
int result = num == 0 ? this.name.compareTo(o.name) : num;// 注意name是字符串需要通过comparaTo方法比较
return result;// 当判断集合中已经有了,那么不会覆盖只会丢弃,保留原来的值
}
- 在此运行测试得到结果,这样只要不是全部相同,就会存储并进行排序。
[Student{name='Java', grade=20}, Student{name='JDK', grade=21}, Student{name='JRE', grade=22}, Student{name='Java', grade=22}, Student{name='JVM', grade=23}]
Set集合的遍历方式
需要注意的是set集合是哈希表、链表、树存储所以就没有索引,即不能通过索引遍历,那么只有两种遍历方式
①增强for循环遍历②通过迭代器遍历
Iterator<Student> iter = set.iterator();// 可以传参,按第几个开始遍历
// 由于迭代器不知道需要遍历多少次,所以之能通过while,通过hashNext方法进行判断是否有下一个值
while(iter.hasNext())
System.out.print(iter.next());// 这个指针一直往下移,代表当前的引用对象
System.out.println("---------------");
// 增强for循环
for (Student s: set) {
System.out.print(s);
}
通过上面对HashSet进行测试运行结果
Student{name='JDK', grade=21}
Student{name='JRE', grade=22}
Student{name='JVM', grade=23}
Student{name='Java', grade=20}
---------------
Student{name='JDK', grade=21}
Student{name='JRE', grade=22}
Student{name='JVM', grade=23}
Student{name='Java', grade=20}
诡异的重复值
package com.wenhua.project;
import java.util.HashSet;
import java.util.Objects;
public class UpdateSet {
public static void main(String[] args) {
HashSet<Person> set = new HashSet<Person>();
Person ava= new Person(21,"java");
Person jdk= new Person(20,"jdk");
set.add(ava);
set.add(jdk);
// 修改wenhau的值
ava.setName("Djava") ;
System.out.println(set.contains(ava));
set.remove(ava);// 并没有删除成功,因为传入的地址
System.out.println(set);
System.out.println(set.contains(new Person(21,"Djava")));
set.add(new Person(21,"Djava"));
System.out.println(set);
System.out.println(set.contains(new Person(21,"java")));
set.add(new Person(21,"java"));
System.out.println(set);
}
}
class Person {
private Integer id;
private String name;
public Person(Integer id, String name) {
this.id = id;
this.name = name;
}
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;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return id.equals(person.id) &&
name.equals(person.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name);
}
@Override
public String toString() {
return "person{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
运行结果
false
[person{id=21, name='Djava'}, person{id=20, name='jdk'}]
false
[person{id=21, name='Djava'}, person{id=20, name='jdk'}, person{id=21, name='Djava'}]
false
[person{id=21, name='Djava'}, person{id=21, name='java'}, person{id=20, name='jdk'}, person{id=21, name='Djava'}]