Set集合
想要学习其他关于Java集合知识,查看我上期博客:Collection集合,List集合
Set集合概述:
Set 接口是 Collection 的子接口,set 接口没有提供额外的方法
Set 集合不允许包含相同的元素,如果试把两个相同的元素加入同一个 Set 集合中,则添加操作失败。
Set 判断两个对象是否相同不是使用 == 运算符,而是根据 equals() 方法进行判断
Set 存储是无序的、不可存储重复的数据。
Set集合特点:
-
特点:无序,不重复
-
遍历:foreach,迭代器
-
扩容: 初始容量16,负载因子0.75,扩容增量1倍
Set集合中的实现类:
HashSet实现类:
HashSet 是 Set 接口的典型实现,大多数时候使用 Set 集合时都使用这个实现类。HashSet 按 Hash 算法来存储集合中的元素,因此具有很好的存取、查找、删除性能。
HashSet 集合判断两个元素相等的标准:两个对象通过 hashCode() 方法比较相等,并且两个对象的 equals() 方法返回值也相等。
对于存放在 Set 容器中的对象,对应的类一定要重写 equals() 和 hashCode(Object obj) 方法,以实现对象相等规则。即:“相等的对象必须具有相等的散列码”。
HashSet 具有以下特点:
不能保证元素的排列顺序
HashSet 不是线程安全的
集合元素可以是 null
案例:
public class Demo1 {
public static void main(String[] args) {
Set<String> set = new HashSet<>();
//set集合存储的无序的 不可重复的
set.add("b");
set.add("a");
set.add("d");
set.add("c");
set.add("a");
set.add("ad");
System.out.println(set);
Set<Integer> set1 = new HashSet<>();
set1.add(78);
set1.add(23);
set1.add(100);
set1.add(56);
System.out.println(set1);
set1.remove(23);
//循环
for (Integer integer : set1) {
System.out.println(integer);
}
}
}
HashSet集合中存对象:
案例:
class Person {
int id;
String name;
public Person(int id, String name) {
this.id = id;
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 == person.id && Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name);
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
public class Demo2 {
public static void main(String[] args) {
Person person1 = new Person(1, "张三");
Person person2 = new Person(1, "李四");
Person person3 = new Person(1, "李四");
Person person4 = new Person(1, "李四");
//只能存入一个内容相同的数据
//所以就要 用到 equals 和 hashCode
Set<Person> set = new HashSet<>();
//在调用add方法的时候 底层在调用hashCode方法和equals
set.add(person1);
set.add(person2);
set.add(person3);
set.add(person4);
System.out.println(set);
}
}
重写 hashCode() 方法:
在程序运行时,同一个对象多次调用 hashCode() 方法应该返回相同的值。
当两个对象的 equals() 方法比较返回 true 时,这两个对象的 hashCode() 方法的返回值也应相等。
对象中用作 equals() 方法比较的 Field,都应该用来计算 hashCode 值。
重写equals()方法:
当一个类有自己特有的“逻辑相等”概念,当改写 equals() 的时候,总是要改写 hashCode(),根据一个类的 equals 方法(改写后),两个截然不同的实例有可能在逻辑上是相等的,但是,根据 Object.hashCode() 方法,它们仅仅是两个对象。
总之在重写equals方法,就必须重写hashCode方法。
TreeSet实现类:
一个包含有序的且没有重复元素的集合
作用是提供有序的Set集合,自然排序或者根据提供的Comparator进行排序
TreeSet是基于TreeMap实现的
TreeSet中的新增的方法:
Comparator comparator()
Object first()
Object last()
Object lower(Object e)
Object higher(Object e)
SortedSet subSet(fromElement, toElement)
SortedSet headSet(toElement)
SortedSet tailSet(fromElement)
public class Demo1 {
public static void main(String[] args) {
//TreeSet在存储的数据的时候 会排序
Set<Integer> set = new TreeSet<>();
set.add(89);
set.add(79);
set.add(69);
set.add(109);
set.add(39);
System.out.println(set);
Set<String> set1 = new TreeSet<>();
set1.add("d");
set1.add("w");
set1.add("a");
set1.add("c");
System.out.println(set1);
}
}
TreeSet中的排序的原因分为自然排序和定制排序(存入对象):
定制排序:
TreeSet 的自然排序要求元素所属的类实现 Comparable 接口,如果元素所属的类没有实现Comparable 接口,或不希望按照升序(默认情况)的方式排列元素或希望按照其它属性大小进行排序,则考虑使用定制排序。定制排序,通过 Comparator 接口来实现。需要重写 compare(T o1,T o2) 方法。
利用 int compare(T o1,T o2) 方法,比较 o1 和 o2 的大小:如果方法返回正整数,则表示 o1 大于 o2;如果返回 0,表示相等;返回负整数,表示 o1 小于 o2。
要实现定制排序,需要将实现 Comparator 接口的实例作为形参传递给 TreeSet 的构造器。
此时,仍然只能向 TreeSet 中添加类型相同的对象。否则发生 ClassCastException 异常。
使用定制排序判断两个元素相等的标准是:通过 Comparator 比较两个元素返回了 0。
案例:
class Student implements Comparable<Student>{
String name;
int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Student o) {
System.out.println("123");
int num = this.age - o.age;
return num;
}
}
public class Demo2 {
public static void main(String[] args) {
Student stu1 = new Student("老邢", 45);
Student stu2 = new Student("老邢", 35);
Student stu3 = new Student("saolei", 25);
Student stu4 = new Student("老万", 87);
//按照年龄进行排序 存到TreeSet集合中
Set<Student> set = new TreeSet<>();
set.add(stu1);
set.add(stu2);
set.add(stu3);
set.add(stu4);
System.out.println(set);
}
}
总结:
1.HashSet存对象的时候,一定在类中重写equals和hashCode方法
2.TreeSet存对象的时候,一定要实现一个接口Comparable,重写compareTo方法
比较两个对象某个属性的int类型差值
3.equals ,hashCode 都是Object类中的方法,子类都重写了该些方法