前言
大家好,今天跟大家分享一下java中用的比较多得集合,之前我们学过一个叫Arraylist的集合,这次我们将进入美丽的集合世界。
目录
一 、集合概述
二、List集合
2.1 list集合概述和特点
三、集合的遍历方法 - 迭代器
四、Set集合
4.1TreeSet集合概述和特点
4.2.自然排序Comparable
4.3比较器comparetor的使用
五、HashSet集合
5.1HashSet集合概述和特点
5.2HashSet集合保证元素唯一性的原理.
5.3 HashSet实例:
上期问题答案:
所谓集合,就是提供一种存储空间可变的存储模型,存储的数据容量可以随时发生改变。
集合的结构我们首先需要了解,下面这张图就为大家解释了集合的结构。
那么我们今天主要了解的就是List集合和Set集合
list集合概述
- 有序集合(也称为序列),用户可以精确控制列表中每个元素的插入位置。用户可以通过整数索引访问元素,并搜索列表中的元素
- 与Set集合不同,列表通常允许重复的元素
list集合特点
- 有索引
- 可以存储重复元素
- 元素存取有序
学集合,我们应该详细的去了解该集合之中包含的方法,去完成相应的操作
以下是list集合的常用方法
那么我们要注意的是List是作为集合接口存在的,也就是说List集合是无法被实例化,通过对象调方法的,那么我们应该想到,多态的方法来完成List集合的实例化,那么有很多类都是List集合的实例对象, 我们只介绍ArrayList集合和LinkedList集合。
Arraylist集合相信大家在使用他的时候会发现,他跟数组有点像,好像就是数组的无限制版,但其实他的底层就是通过数组结构来实现的,当然他也延续了数组的优秀传统,就是查询较快,通过索引,但是他的增删就很慢,这是因为他的每一次查询只需要通过索引获取其值,而他的增删需要其增删的值向被删的位置移动,或者依次移出被插入的元素的位置,这样就导致了我们程序运行的时间较长,就有了我们的LinkedList集合。
linkedList集合的底层是链表结构实现的,查询慢,增删快。
那么我们定义了一个集合之后,我们想要输出,除了直接输出集合,如果集合中是存储的基本数据类型的元素,那么会通过他们各自的toString方法输出,那么我们集合当中存储的是一个对象呢?有人说道了,那我们也可以通过重写toString 方法来输出啊。确实,你可以通过重写toString方法,但是,你无法通过如此来get集合当中的每一个元素,如果你不重写toSring方法的话,那么你输出的就是一个地址值,因此我们为了我们的需求,就应该学习集合的遍历方式。
之前我们学过了ArrayList集合的遍历方式,那是因为其底层是由数组实现的,可以通过数组的方式进行遍历,那么集合有没有属于他专有的遍历方式呢,诶,这你就问对人了,还真有,那就是迭代器。
迭代器的介绍
- 迭代器,集合的专用遍历方式
- Iterator
- 迭代器是通过集合的iterator()方法得到的,所以我们说它是依赖于集合而存在的
对于迭代器遍历,我们来看一个例子加深一下印象
public class IteratorDemo {
public static void main(String[] args) {
//创建集合对象
Collection c = new ArrayList<>();
//添加元素
c.add("hello");
c.add("world");
c.add("java");
c.add("javaee");
//Iterator iterator():返回此集合中元素的迭代器,通过集合的iterator()方法得到
Iterator it = c.iterator();
//用while循环改进元素的判断和获取
while (it.hasNext()) {
String s = it.next();
System.out.println(s);
}
}
}
那么该程序就会输出
hello
world
java
javaee
TreeSet集合概述
- 元素有序,可以按照一定的规则进行排序,具体排序方式取决于构造方法
- TreeSet():根据其元素的自然排序进行排序
- TreeSet(Comparator comparator) :根据指定的比较器进行排序
- 没有带索引的方法,所以不能使用普通for循环遍历
- 由于是Set集合,所以不包含重复元素的集合
TreeSet集合基本使用
import java.util.TreeSet;
import java.util.Iterator;
/*
TreeSet集合特点
1:元素有序,这里的顺序不是指存储和取出的顺序,而是按照一定的规则进行排序,具体排序方式取决于构造方法
TreeSet():根据其元素的自然排序进行排序
TreeSet(Comparator comparator) :根据指定的比较器进行排序
2:没有带索引的方法,所以不能使用普通for循环遍历
3:由于是Set集合,所以不包含重复元素的集合
*/
public class TreeSetDemo01 {
public static void main(String[] args) {
TreeSet t = new TreeSet();
t.add(10);
t.add(20);
t.add(50);
t.add(30);
t.add(40);
t.add(10);
//迭代器遍历
System.out.println("迭代器遍历:");
Iterator it = t.iterator();
while (it.hasNext()) {
int i = it.next();
System.out.println(i);
}
//增强for遍历
System.out.println("增强for遍历:");
for (Integer i : t) {
System.out.println(i);
}
}
}
上述程序的输出结果:
迭代器遍历:
10
20
30
40
50
增强for遍历:
10
20
30
40
50
那么我们可以看到,重复元素10 并没有存入集合,而且上述程序的输出结果是并没有按我们的存入顺序输出,他是按照自然顺序进行排列的,是因为Integer类实现了Comparable接口并且重写了他的compareTo()方法,这里我们可以通过查看源码:
public final class Integer extends Number implements Comparable {
public int compareTo(Integer anotherInteger) {
return compare(this.value, anotherInteger.value);
}
}
那如果我们自己写了一个类是不是也可以这么做呢?
所谓自然排序,就是让集合元素所属的类实现Comparable接口,并重写compareTo()方法,那么我们的重写规则就是由我们自己定义,但是他的排序是按照比如说数字的大小进行升序或者降序,字母表的排序等等,接下来我们就实战来感受一下:
案例需求
- 存储学生对象并遍历,创建TreeSet集合使用无参构造方法
- 要求:按照年龄从大到小排序,年龄相同时,按照姓名的字母顺序排序
代码示例:
学生类
public class Student implements Comparable {
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 int compareTo(Student s) {
// return -1;
// int num = this.age - s.age;//升序
int num = s.age - this.age;//降序
// return num;
int num2 = num == 0 ? this.name.compareTo(s.name) : num;
return num2;
}
}
测试类:
import java.util.TreeSet;
/*
存储学生对象并遍历,创建集合使用无参构造方法
要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
*/
public class TreeSetDemo02 {
public static void main(String[] args) {
TreeSet t = new TreeSet();
Student s1 = new Student("张三", 18);
Student s2 = new Student("李四", 22);
Student s3 = new Student("王五", 26);
Student s4 = new Student("刘二", 29);
Student s5 = new Student("李四", 35);
Student s6 = new Student("朱三", 29);
t.add(s1);
t.add(s2);
t.add(s3);
t.add(s4);
t.add(s6);
t.add(s5);
for (Student s : t) {
System.out.println(s.getName() + "," + s.getAge());
}
}
}
输出结果:
李四,35
刘二,29
朱三,29
王五,26
李四,22
张三,18
大家可以看到,我们的输出结果是优先按照年龄的降序来排序的,年龄相同时就按姓名的字母顺序排列,这里我们的重写compareTo()方法就是我们自定义排序的关键
@Override
public int compareTo(Student s) {
// return -1;
// int num = this.age - s.age;//升序
int num = s.age - this.age;//降序
// return num;
int num2 = num == 0 ? this.name.compareTo(s.name) : num;
return num2;
}
那么String的compareTo()方法如何判定两个变量,并且返回值的呢?感兴趣的大佬可以研究一下源码,我给你们找来了,这里就不多做介绍了。
public int compareTo(String anotherString) {
int len1 = value.length;
int len2 = anotherString.value.length;
int lim = Math.min(len1, len2);
char v1[] = value;
char v2[] = anotherString.value;
int k = 0;
while (k < lim) {
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
return c1 - c2;
}
k++;
}
return len1 - len2;
}
comparetor我们就直接上例子理解,至于源码我这次就不放上了,感兴趣的老铁可以自己在做题的时候,Ctrl + B一下,就能看到。
案例需求
- 存储学生对象并遍历,创建TreeSet集合使用带参构造方法
- 要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
实现步骤
- 用TreeSet集合存储自定义对象,带参构造方法使用的是比较器排序对元素进行排序的
- 比较器排序,就是让集合构造方法接收Comparator的实现类对象,重写compare(T o1,T o2)方法
- 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
注意:这里实现重写比较器的compare方法可以使用匿名内部类的方法来做,不会的小伙伴也别担心,我给大家提供了传统的实现类方法
学生类
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;
}
}
学生实现comparetor接口类
import java.util.Comparator;
/**
* @author wang
* @className StudentDemo
* @date 2021/10/11 15:31
*/
public class StudentDemo implements Comparator {
//这里演示的是随着年龄升序排序
@Override
public int compare(Student s1, Student s2) {
int num = s1.getAge() - s2.getAge();
int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
return num2;
}
}
测试类
import java.util.Comparator;
import java.util.TreeSet;
/*
存储学生对象并遍历,创建TreeSet集合使用带参构造方法
要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
*/
public class TreeSetDemo {
public static void main(String[] args) {
//通过多态的形式实现接口
// Comparator sd = new StudentDemo();
// //将接口实现类比较器传入集合构造器中,也可直接使用匿名内部类;
// TreeSet t = new TreeSet(sd);
//匿名内部类演示随着年龄降序排序
TreeSet t = new TreeSet(new Comparator() {
@Override
public int compare(Student s1, Student s2) {
int num = s2.getAge() - s1.getAge();
int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
return num2;
}
});
Student s1 = new Student("张三", 18);
Student s2 = new Student("李四", 22);
Student s3 = new Student("王五", 26);
Student s4 = new Student("刘二", 29);
Student s5 = new Student("李四", 35);
Student s6 = new Student("朱三", 29);
t.add(s1);
t.add(s2);
t.add(s3);
t.add(s4);
t.add(s6);
t.add(s5);
for (Student s : t) {
System.out.println(s.getName() + " " + s.getAge());
}
}
}
输出结果:
李四 35
刘二 29
朱三 29
王五 26
李四 22
张三 18
HashSet集合的特点
- 底层数据结构是哈希表
- 对集合的迭代顺序不作任何保证,也就是说不保证存储和取出的元素顺序一致
- 没有带索引的方法,所以不能使用普通for循环遍历
- 由于是Set集合,所以是不包含重复元素的集合
HashSet集合的基本使用
public class HashSetDemo01 {
public static void main(String[] args) {
//创建集合对象
HashSet hs = new HashSet();
//添加元素
hs.add("hello");
hs.add("world");
hs.add("java");
hs.add("world");
//遍历
for(String s : hs) {
System.out.println(s);
}
}
}
1.根据对象的哈希值计算存储位置
如果当前位置没有元素则直接存入
如果当前位置有元素存在,则进入第二步
2.当前元素的元素和已经存在的元素比较哈希值
如果哈希值不同,则将当前元素进行存储
如果哈希值相同,则进入第三步
3.通过equals()方法比较两个元素的内容
如果内容不相同,则将当前元素进行存储
如果内容相同,则不存储当前元素
这里给大家放一张图方便大家理解
那么在实际案例中我们如何去保证集合的元素的唯一性呢?
案例需求
- 创建一个存储学生对象的集合,存储多个学生对象,使用程序实现在控制台遍历该集合
- 要求:学生对象的成员变量值相同,我们就认为是同一个对象
学生类
import java.util.Objects;
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 "姓名:" + getName() + "\t年龄:" + getAge();
}
@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);
}
}
测试类
import java.util.HashSet;
import java.util.Iterator;
/*
需求:
创建一个存储学生对象的集合,存储3个学生对象,使用程序实现在控制台遍历该集合
要求:学生对象的成员变量值相同,我们就认为是同一个对象
思路:
1:定义学生类
2:创建HashSet集合对象
3:创建学生对象
4:把学生添加到集合
5:遍历集合(增强for)
*/
public class HashSetDemo02 {
public static void main(String[] args) {
HashSet hs = new HashSet();
Student s1 = new Student("诸葛亮", 20);
Student s2 = new Student("关羽", 22);
Student s3 = new Student("刘备", 33);
Student s4 = new Student("刘备", 33);
hs.add(s1);
hs.add(s2);
hs.add(s3);
hs.add(s4);
Iterator it = hs.iterator();
while (it.hasNext()) {
Student s = it.next();
System.out.println(s);
}
}
}
输出结果
姓名:诸葛亮 年龄:20
姓名:关羽 年龄:22
姓名:刘备 年龄:33
可以看到我们有一个重复的元素刘备并没有被存储进来,这就是因为我们在学生类中重写了hashCode方法和equals方法。
D
100
100
200
原因是因为静态成员随着类加载而加载,所以静态成员先输出
你答对了吗?记得点赞关注加三连啊亲!