文章目录
- 1.Set接口概述
- 2.HashSet集合
- 2.1.HashSet集合存储数据的结构(哈希表)
- 2.2.HashSet存储JavaAPI中的类型元素
- 2.3.HashSet存储自定义类型元素
- 3.TreeSet集合
- 3.1.自然排序Comparable(内部比较器)
- 3.2.比较器排序Comparator(外部比较器)
- 3.3.自然排序和比较器排序比较
- 3.4.TreeSet练习
- 4.LinkedHashSet集合
- 4.1.特点
- 5.总结
1.Set接口概述
- Set集合可以去除重复元素
- Set集合的存入顺序和取出顺序不一致
- Set集合没有索引,不能使用普通for循环遍历
查阅Set集合的API介绍,通过元素的equals
方法,来判断是否为重复元素,它是个不包含重复元素的集合。Set集合取出元素的方式可以采用:迭代器、增强for。
Set集合有多个子类,这里我们介绍其中的HashSet
、TreeSet
、LinkedHashSet
这两个集合。
该集合中没有特有的方法,直接继承自Collection
。
---| Itreable 接口 实现该接口可以使用增强for循环
---| Collection 描述所有集合共性的接口
---| List接口 可以有重复元素的集合
---| ArrayList
---| LinkedList
---| Set接口 不可以有重复元素的集合
- 案例:set集合添加元素并使用迭代器迭代元素。
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class Demo4 {
public static void main(String[] args) {
//Set 集合存和取的顺序不一致。
Set hs = new HashSet();
hs.add("世界军事");
hs.add("兵器知识");
hs.add("舰船知识");
hs.add("汉和防务");
System.out.println(hs);
// [舰船知识, 世界军事, 兵器知识, 汉和防务]
Iterator it = hs.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
}
}
Set接口下面,没有定义get( )方法,那么就意味着Set没有下标。
2.HashSet集合
2.1.HashSet集合存储数据的结构(哈希表)
.首先明确一点,hashset实现了set接口,因此是无序的,这个无序是指,元素输入和输出的顺序。也就是不管添加元素的顺序是什么,遍历输出该集合时,都是按照存储的顺序来的,一般输出顺序和输入顺序不同,即使相同也是巧合。
第一个:
HashSet list = new HashSet();
list.add(1);
list.add(2);
list.add(17);
list.add(33);
结果:
1
17
33
2
第二个:
HashSet<Integer> list = new HashSet<>();
list.add(1);
list.add(17);
list.add(2);
list.add(33);
结果:
1
17
33
2
结论:输入输出,是无序的,素的存储是按照一定机制来的,输出就是按照这个顺序,因此这两个输出结果顺序一样。
只有搞懂hashset的存储机制原理才能够了解输出顺序
hashset的存储过程
1.首先获取要存储元素的hash值,
2.然后用hash值 %(模)表的长度(哈希表初始数组长度为16,加载因子0.75,每次扩容1倍,也就是说,当元素个数大于16*0.75=12时,数组长度扩大一倍,变成32)得到的余数为数组的下标。然后将元素加到此中。
3.如果不同的元素经过2步骤处理后得到的余数相同,则之前在这个位置的元素,挂到新元素下面(形成一个链表),如果,下挂元素超过8个,则链表转换为红黑树。
代码分析
HashSet<Integer> list = new HashSet<>();
list.add(1);
list.add(2);
list.add(17);
list.add(33);
结果:
1
17
33
2
1.添加1时,对元素 模 表长度 也就是1%16 余1,在数组下标为1的位置插入元素1
2.添加2时,对元素 模 表长度 也就是2%16 余2,在数组下标为2的位置插入元素2
3.添加17时,对元素 模 表长度 也就是17%16 余1,在数组下标为1的位置插入元素17,元素17下挂到元素1下(尾插法)
4.添加33时,对元素 模 表长度 也就是33%16 余1,在数组下标为1的位置插入元素33,元素33下挂到元素17下
此时遍历集合,按照数组下标升序输出,同一下标有多个值,从上往下依次输出,
所以输出为 1,17,33,2
2.2.HashSet存储JavaAPI中的类型元素
给HashSet
中存储JavaAPI
中提供的类型元素时,不需要重写元素的hashCode
和equals
方法,因为这两个方法,在JavaAPI
的每个类中已经重写完毕,如String类、Integer类等。
- 创建
HashSet
集合,存储String对象。
publicclass HashSetDemo {
publicstaticvoid main(String[] args) {
//创建HashSet对象
HashSet<String> hs = new HashSet<String>();
//给集合中添加自定义对象
hs.add("zhangsan");
hs.add("lisi");
hs.add("wangwu");
hs.add("zhangsan");
//取出集合中的每个元素
Iterator<String> it = hs.iterator();
while(it.hasNext()){
String s = it.next();
System.out.println(s);
}
}
}
输出结果如下,说明集合中不能存储重复元素:
wangwu
lisi
zhangsan
2.3.HashSet存储自定义类型元素
给HashSet
中存放自定义类型元素时,需要重写对象中的hashCode
和equals
方法,建立自己的比较方式,才能保证HashSet
集合中的对象唯一
- 创建自定义对象Student
public class Student {
private String name;
private int age;
public Student(String name, int age) {
super();
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);
}
}
- 创建HashSet集合,存储Student对象。
public class HashSetDemo {
public static void main(String[] args) {
//创建HashSet对象
HashSet hs = new HashSet();
//给集合中添加自定义对象
hs.add(new Student("zhangsan", 21));
hs.add(new Student("lisi", 22));
hs.add(new Student("wangwu", 23));
hs.add(new Student("zhangsan", 21));
//取出集合中的每个元素
Iterator it = hs.iterator();
while (it.hasNext()) {
Student s = (Student) it.next();
System.out.println(s);
}
}
}
输出结果如下,说明集合中不能存储重复元素:
Student [name=lisi, age=22]
Student [name=zhangsan, age=21]
Student [name=wangwu, age=23]
问题:现在有一批数据,要求不能重复存储元素,而且要排序。ArrayList 、 LinkedList不能去除重复数据。HashSet可以去除重复,但是是无序。
3.TreeSet集合
不可以存储重复元素
没有索引,不能使用普通for循环
可以将元素按照规则进行排序(自然排序和比较器排序)
- 案例:使用TreeSet集合存储字符串元素,并遍历
import java.util.TreeSet;
public class Demo5 {
public static void main(String[] args) {
TreeSet ts = new TreeSet();
ts.add("ccc");
ts.add("aaa");
ts.add("ddd");
ts.add("bbb");
System.out.println(ts); // [aaa, bbb, ccc, ddd]
}
}
既然TreeSet
可以自然排序,那么TreeSet
必定是有排序规则的。
使用元素的自然顺序对元素进行排序,或者根据创建 set时提供的 Comparator
进行排序(比较器排序)
public static void main(String[] args) {
TreeSet 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);
}
3.1.自然排序Comparable(内部比较器)
- 使用空参构造创建
TreeSet
集合 - 自定义的Student类实现
Comparable
接口 - 重写接口中的
compareTo
方法
public class Student implements Comparable {
private String name;
private int age;
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", 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;
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student() {
}
@Override
public int compareTo(Object o) {
Student s = (Student) o;
//主要判断条件
int result = this.age - s.age;
//次要判断条件
result = this.age == s.age ? this.name.compareTo(s.name) : result;
return result;
}
}
测试类:
public class TreeSetDemo {
public static void main(String[] args) {
TreeSet students = new TreeSet();
Student stu1 = new Student("zhangliang", 59);
Student stu2 = new Student("likui", 43);
Student stu3 = new Student("abc", 38);
Student stu4 = new Student("acb", 38);
Student stu5 = new Student("zhouyu", 26);
students.add(stu1);
students.add(stu2);
students.add(stu3);
students.add(stu4);
students.add(stu5);
for (Object student : students) {
System.out.println(student);
}
}
}
3.2.比较器排序Comparator(外部比较器)
TreeSet
集合的构造方法接收Comparator
的实现类对象(匿名内部类),重写compare
方法
案例需求:
使用TreeSet
集合存储老师对象并遍历,创建TreeSet
集合使用带参构造方法
要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
public class Teacher {
private String name;
private int age;
@Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
", 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;
}
public Teacher(String name, int age) {
this.name = name;
this.age = age;
}
public Teacher() {
}
}
测试类
public class TreeSetDemo1 {
public static void main(String[] args) {
//使用匿名内部类创建Comparator实现类,重写compare方法
TreeSet teachers = new TreeSet(new MyComparator());
Teacher teacher1 = new Teacher("abc", 26);
Teacher teacher2 = new Teacher("acb", 26);
Teacher teacher3 = new Teacher("wangling", 19);
Teacher teacher4 = new Teacher("deshrng", 56);
Teacher teacher5 = new Teacher("hangjinh", 36);
teachers.add(teacher1);
teachers.add(teacher2);
teachers.add(teacher3);
teachers.add(teacher4);
teachers.add(teacher5);
for (Object teacher : teachers) {
System.out.println(teacher);
}
}
}
class MyComparator implements Comparator {
@Override
public int compare(Object o1, Object o2) {
Teacher t1 = (Teacher) o1;
Teacher t2 = (Teacher) o2;
int result = t1.getAge() - t2.getAge();
result = result == 0 ? t1.getName().compareTo(t2.getName()) : result;
return result;
}
}
3.3.自然排序和比较器排序比较
- 自然排序: 自定义类实现
Comparable
接口,重写compareTo
方法,根据返回值进行排序 - 比较器排序: 创建
TreeSet
对象的时候传递Comparator
的实现类对象,重写compare
方法,根据返回值进行排序
两种方式中关于返回值的规则:
如果返回值为负数,表示当前存入的元素是较小值,存左边
如果返回值为0,表示当前存入的元素跟集合中元素重复了,不存
如果返回值为正数,表示当前存入的元素是较大值,存右边
注意:当Comparable比较方式和Comparator比较方式同时存在时,以Comparator的比较方式为主;
3.4.TreeSet练习
将字符串中的数值进行排序。
例如
String str="8 10 15 5 2 7"; // 2,5,7,8,10,15
- 使用 TreeSet完成。
1,将字符串切割。
2,可以将这些对象存入TreeSet集合。
因为TreeSet自身具备排序功能。
public class Demo5 {
public static void main(String[] args) {
String str = "8 10 15 5 2 7";
String[] strs = str.split(" ");
TreeSet ts = new TreeSet();
for (int x = 0; x < strs.length; x++) {
int y = Integer.parseInt(strs[x]);
ts.add(y);
}
System.out.println(ts);
}
}
4.LinkedHashSet集合
public class LinkedHashSet<E> extends HashSet<E>
implements Set<E>, Cloneable, java.io.Serializable
4.1.特点
- 此类在java.util包中可用。
- 这是Set接口的实现类。
- LinkedHashSet类是HashSet类的子级。
- 在LinkedHashSet中,保留插入顺序,这意味着元素的插入顺序必须与元素的检索顺序相同。
- 在Java 1.4的早期版本中引入了LinkedHashSet类。
- 如果元素的插入顺序很重要,则应使用LinkedHashSet。
示例
假设我们有一个包含少量元素的LinkedHashSet
。在这里,我们以[10,20,30,50,null]的顺序添加元素,如果我们要检索元素,则检索元素的顺序必须相同(即,其插入和检索顺序必须相同)。元素。)因此输出将相同,并且顺序将类似于[10,20,30,50,null]。
class LinkedHashSetClass {
public static void main(String[] args) {
//创建LinkedHashSet的实例
LinkedHashSet lhs = new LinkedHashSet();
//通过使用add()方法将元素添加到LinkedHashSet-
lhs.add(10);
lhs.add(20);
lhs.add(30);
lhs.add(50);
lhs.add(null);
//显示LinkedHashSet元素
System.out.println("LinkedHashSet is :" + lhs);
}
}
5.总结
- 看到
array
,就要想到小标。 - 看到link,就要想到
first
,last
。 - 看到hash,就要想到
hashCode
,equals
. - 看到tree,就要想到两个接口。
Comparable
,Comparator
。