JavaSE---collection的子接口之二Set接口
1、Set接口概述,继承Collection接口的方法
Collection接口:单列集合,用来存储一个一个对象
* Set接口:存储无序的,不可重复的数据 ---->高中讲的“集合”
* HashSet:作为Set接口的主要实现类;线程不安全的,可以存储null值
* LinkedHashSet:作为HashSet的子类,遍历器内部数据时,可以按照添加的顺序遍历。
* 对于频繁的遍历操作,LinkedHashSet效率高于HashSet
* TreeSet:可以按照添加对象的指定属性,进行排序。
*
* 1、Set接口中没有额外定义新的方法,使用的都是Collection中声明过的方法
*
* 2、要求:向Set中添加的数据,其所在的类一定要重写hashCode()和equals()
* 要求:的hashCode()和equals()尽可能保持一致性:相同的对象必须具有相同的散列码
1.1、Set接口的无序性证明
无序性指的是添加元素的时候,元素的存放位置并不是一个挨着一个存放的,而是没有顺序的存放的。
1.2、Set接口不可重复的
Set:存储无序的、不可重复的数据
* 以HashSet为例说明:
* 1、无序性:不等于随机性。存储的数据结构在底层数组中并非按照数组索引的添加顺序添加、而是跟数组的哈希值。
*
* 2、不可重复性:保证添加的元素按照equals()判断时,不能返回true,即:相同的元素只能添加一个。
*
* 二、添加元素的过车:以HashSet为例:
* 我们向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算元素a的哈希值,
* 此哈希值接着通过某种算法计算出在HashSet底层数组中存放位置(即为:索引位置),判断
* 数组此位置上是否已经有元素:
* 如果此位置上没有其他元素,则元素a添加成功。--->情况1
* 如果此元素位置上有其他元素b(或以链表形式存在的多个元素),则比较元素a与元素b的hash值::
* 如果hash值不相同,则元素a添加成功。---->情况2
* 如果hash相同,进而需要调用元素a所在类的equals()方法。
* equals()返回true,元素a添加失败
* equals()返回false,则元素a添加成功。--->情况3
* 对于添加成功的情况2和情况3而言:元素a与已经存在指定索引位置上的数据以链表的方式存储。
* jdk7:元素a放到数组中,指向原来的元素
* jdk8:原来的元素在数组中,指向元素b
* HashSet底层:数组加链表
2、Set接口实现类之一HashSet
底层实现:数组+链表
3、Set接口实现类之二LinkedHashSet
底层实现:数组+链表
- LinkedHashSet作为HashSet的子类,在添加数据同时,每个数组还维护了两个引用,记录此数据前一个数据和后一个数据。
- 优点:对于频繁的遍历操作,LinkedHashSet效率高于HashSet
4、Set接口实现类之三TreeSet
采取红黑树存储结构
package com.collection.test.collection;
import org.junit.Test;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
/**
* 1、向TreeSet中添加的数据,要求是相同类的对象
* 2、两种排序方式:自然排序 和 定制排序
* 3、自然排序中,比较两个对象是否相同的标准为:compareTo()返回0,不再是equals().
* 4、定制排序中,比较两个对象是否相同的标准为:compareTo()返回0,不再是equals().
*
* DATE: 2022/4/17 16:38
*/
public class TreeSet01 {
// 1、自然排序:在Person实体类中重写compareTo方法
@Test
public void test01(){
Set set = new TreeSet();
set.add(new Person("Tom","18"));
set.add(new Person("Jack","16"));
set.add(new Person("Marry","19"));
set.add(new Person("Rose","20"));
set.add(new Person("Mike","12"));
set.add(new Person("Mike","23"));
Iterator iterator = set.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
// 2、定制排序
@Test
public void test02(){
Comparator com = new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if(o1 instanceof Person && o2 instanceof Person){
Person person1 = (Person) o1;
Person person2 = (Person) o2;
return person1.getAge().compareTo(person2.getAge());
}else {
throw new RuntimeException("输入的类型不匹配");
}
}
};
Set set = new TreeSet(com);
set.add(new Person("Tom","18"));
set.add(new Person("Jack","16"));
set.add(new Person("Marry","19"));
set.add(new Person("Rose","20"));
set.add(new Person("Mike","12"));
Iterator iterator = set.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
Person类
//按照姓名从a到z排序
@Override
public int compareTo(Object o) {
if(o instanceof Person){
Person person = (Person) o;
int compare = this.name.compareTo(person.name);
if(compare != 0){
return compare;
}else{
return this.age.compareTo(person.age);
}
}else{
throw new RuntimeException("输入的餐厨类型有误");
}
}
//按照姓名从z到a排序
// @Override
// public int compareTo(Object o) {
// if(o instanceof Person){
// Person person = (Person) o;
// return -this.name.compareTo(person.name);
// }else{
// throw new RuntimeException("输入的餐厨类型有误");
// }
// }