今日内容
- 集合的概述和Collection集合
- List集合
- 数据结构和ArrayList集合的相关案例
01. 为什么会出现集合类
- 1. 集合的特点
- 2. 为什么要有集合
总结:
1. 相同点: 集合和数组都是容器, 用来存储数据的
不同点:
数组存储:可以存储基本数据类型, 也可以存储引用数据类型
存储基本数据类型的时候, 存储的是值 int[] arr = {11,22,33};
Person p1 = new Perosn(); Person p2 = new Person();
存储引用数据类型的时候, 存储的是地址值 Person[] pArr = new Person[2];
pArr[0] = p1; pArr[1] = p2;
集合存储: 只能存储引用数据类型
长度: 数组的长度是固定的, 集合的长度可变的.
2. 举例:
学生管理系统, 记录学生的各项信息
如果使用数组进行存储, 数组长度初始为80个大小, 这时候插班进来的第81个学生就没有位置存储了.
问题产生: 使用数组就不合适了
解决方案: 使用一个长度可变的容器, [集合]
02. 集合类体系结构图
- 再画一张
03. 创建Collection集合对象并添加元素
- 为什么要先学习Collection?
- 创建集合对象的时候, <>代表什么?
总结:
1. Collection是单列集合的根接口, 内部定义的规则, 所有实现类都会具备一份
/*
* 细节1: 如果一个类没有找到我们使用的方法, 可以去父类中查询
*
* 问题: 接口到底有没有继承Object类
*
* 细节2: 接口没有继承Object类, 但是接口底层会有Object类所有方法的引用
*
* 目的: 就是为了方式多态调用方法的编译报错!
*/
2. <> : 泛型(jdk1.5新特性) -> 用于限制集合中存储的数据类型
集合存储: 只能存储引用数据类型, 是因为泛型的特点, <>中只能编写引用数据类型
代码:
public class Demo1_Collection {
public static void main(String[] args) {
// 1. 创建集合对象
// 在jdk1.7版本之后, 右侧的尖括号中可以不写类型, 菱形泛型
// <> : 泛型 -> 用于限制集合中存储的数据类型
Collection<String> c = new ArrayList<>();
// 2. 向集合中添加元素
c.add("abc");
c.add("bbb");
// 3. 展示集合中的元素
System.out.println(c.toString());
}
}
04. Collection集合的成员方法
- boolean add(E e):添加元素
- boolean remove(Object o):从集合中移除元素 ***
- void clear():清空集合中的元素
- boolean contains(Object o):判断集合中是否存在指定的元素 ***
- boolean isEmpty():判断集合是否为空
-
int size():集合的长度,也就是集合中元素的个数
示例代码:
public static void main(String[] args) { // 1. 创建集合对象, 容器中存储的是Person对象 Collection<Person> c = new ArrayList<>(); // 2. 向集合中添加Person对象. c.add(new Person("张三", 23)); c.add(new Person("李四", 24)); c.add(new Person("王五", 25)); // 注意: 如果添加的元素是自定义对象的话, 那么没有重写toString, 将打印地址值 System.out.println(c); // 3. 调用集合的删除方法, 删除张三 // 注意 : remove方法底层依赖于equals方法. c.remove(new Person("张三", 23)); System.out.println(c); // 4. 调用集合中判断是否包含的方法 // 注意 : contains方法底层依赖于equals方法. System.out.println(c.contains(new Person("李四", 24))); // 5. 调用集合的清空方法 c.clear(); // 6. 调用集合判断是否为空的方法 System.out.println(c.isEmpty()); // 7. 打印集合的长度 System.out.println(c.size()); }
05. Collection集合的遍历
- 使用迭代器遍历集合的步骤
总结:
1. 调用集合的iterator方法, 获取迭代器
Iterator<String> it = c.iterator();
2. 使用循环判断集合中是否还有元素
while(it.hasNext()){
}
3. 使用next方法元素取出
while(it.hasNext()){
String s = it.next();
}
示例代码:
public static void main(String[] args) {
Collection<Person> c = new ArrayList<>();
c.add(new Person("张三", 23));
c.add(new Person("李四", 24));
c.add(new Person("王五", 25));
// 1. 获取迭代器对象
Iterator<Person> it = c.iterator();
// 2. 循环判断集合中是否还有元素
while(it.hasNext()){
// 3. 获取元素
Person p = it.next();
System.out.println(p);
}
}
06. 集合(迭代器)使用步骤图解
- 看图说话
07. Collection集合的练习存储自定义对象并遍历
需求: 定义一个标准的学生类, 创建三个学生对象存储到集合容器中, 并遍历集合.
6分钟练习
08. List集合的特点
- List集合有什么特点?
总结:
1. 存取有序
2. 有索引
3. 可以存储重复的
09. List集合的特有成员方法
- void add(int index, E element) :将元素添加到index索引位置上
- E get(int index) :根据index索引获取元素
- E remove(int index) :根据index索引删除元素
- E set(int index, E element):将index索引位置的的元素设置为element
10. List集合的普通for循环遍历
- 案例演示
public class Test2_List {
/*
* List集合特有的一种遍历方式
* 1. get(int index) : 根据传入的索引, 获取指定位置的元素
* 2. size() : 返回集合的长度
*/
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("abc1");
list.add("abc2");
list.add("abc3");
list.add("abc4");
// String s1 = list.get(0);
// String s2 = list.get(1);
// String s3 = list.get(2);
// String s4 = list.get(3);
for(int i = 0; i < list.size(); i++){
System.out.println(list.get(i));
}
}
}
11. List集合的练习存储自定义对象并遍历
- 需求: 定义一个标准的学生类, 创建三个学生对象存储到集合容器中, 并遍历集合.
1. 普通for循环
2. 普通迭代器
3. ListIterator特有的迭代器 --> 基本使用和普通迭代器一致
// 1. 获取列表迭代器
ListIterator<Student> listIt = list.listIterator();
// 2. 判断集合中是否还有元素
while(listIt.hasNext()){
// 3. 调用next获取元素
System.out.println(listIt.next());
}
12. 列表迭代器的特有功能(了解)
- 方法摘要:
总结:
1.boolean hasPrevious() : 判断集合是否还有上一个元素
2.E previous() : 获取集合中的上一个元素.
注意: 特有的功能比较鸡肋
倒序遍历之前, 必须有正序遍历的铺垫, 才能取出来元素
List<Person> list = new ArrayList<>();
list.add(new Person("张三", 23));
list.add(new Person("李四", 24));
list.add(new Person("王五", 25));
// 1. 获取列表迭代器
ListIterator<Person> it = list.listIterator();
// 2. 循环判断是否还有元素
while(it.hasNext()){
// 3. 使用next方法获取元素
System.out.println(it.next());
}
System.out.println("----------------------");
while(it.hasPrevious()){
System.out.println(it.previous());
}
- 5分钟时间练习
13. 并发修改异常产生的原因及解决方案(重点)
- 什么情况下会产生并发修改异常?
- 如何解决?
总结:
1. 当使用迭代器在遍历集合的过程中, 使用集合的添加或删除方法操作元素
就会产生并发修改异常
2. 不让使用集合的添加或删除, 就使用迭代器自身的添加或删除
删除: 普通迭代器中存在
添加: 列表迭代器
扩展:
如果用集合的remove方法删除的是倒数第二个元素的时候, 则不会报出并发修改异常(了解)
原因: 略过了编译器的检查. -> 少调用了一次checkxxxxx方法
14. 增强for(jdk1.5的新特性)的概述和使用
- 增强for循环的格式是?
- 增强for循环底层依赖于什么技术实现?
总结:
1.
for(数据类型 变量名 : 要遍历的数组或集合){
}
2.
增强for循环底层是迭代器实现的
只要是能用迭代器遍历的容器, 都可以使用增强for循环改进.
注意1: 增强for循环不能使用集合的删除方法, 会产生并发修改异常
注意2: 增强for循环中, 不能删除元素
15. 增强for的练习List集合存储自定义对象并遍历
- 案例演示
public static void main(String[] args) {
List<Person> list = new ArrayList<>();
list.add(new Person("张三", 23));
list.add(new Person("李四", 24));
list.add(new Person("王五", 25));
for(Person p : list){
System.out.println(p);
}
}
16. 常见数据结构之栈和队列
- 栈内存结构什么特点?
- 队列内存结构什么特点?
总结:
1: 桟内存结构,类似于手枪弹夹
特点: 先进后出
2: 队列内存结构, 类似于一条管道
特点: 先进先出
17. 常见数据结构之数组和链表
- 数组
优点:查询快, 修改快
原因: 因为数组有索引, 有索引的话, 就可以快速的定位到要操作的元素, 并快速的进行修改.
缺点:增删慢
原因: 因为每一次的添加或删除都需要大批量的操作数据中的元素
删除: 将数组中的元素删除之后, 后面的元素会整体的向前移动一位
假如数组长度为100, 我们将第一个元素进行删除, 后续的99个元素都要进行移动
增加: 以ArrayList为举例, 底层就是数组结构的, 假设底层数组长度为10个大小, 我们在添加第11个元素的时候
底层就会根据原数组, 自动扩容一个1.5倍大小的新数组, 再将数组内容拷贝一份到新数组中去, 随后再把第11个元素进行添加.
- 链表
优点: 增删快
只需要将两个元素的引用切断, 然后重新规划引用即可, 不会影响到过多的元素.
删除也是如此.
缺点: 查询慢, 修改慢
每一次查找元素, 都会在链表结构当中, 逐个的做元素检索.
问题: 查询元素的时候从头开始查, 还是从尾开始查?
底层源码会进行一个判断, 如果离头近, 就从前往后找, 如果离尾近, 从后往前找.
问题: 如何进行的判断?
会根据索引进行折半查找
问题: LinkedList有索引吗?
因为是List派系下的子类,所以存在索引, 但是查找的时候就是不用!
18. List集合子类特点及ArrayList集合存储字符串并遍历
- ArrayList集合的特点, 完成什么样的功能较为合适?
总结:
1.
ArrayList<String> list = new ArrayList<>();
list.add("abc");
list.add("111");
list.add("222");
list.add("333");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
System.out.println("-------------");
Iterator<String> it = list.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
System.out.println("--------------------");
for(String s : list){
System.out.println(s);
}
19. ArrayList集合的练习存储自定义对象并遍历
- 回顾3种集合的迭代方式
三种遍历是否能删除元素.
1. 迭代器: 可以删除, 但是在调用remove方法之前必须调用next方法
问题: next方法会将指针向后移动一位, 为什么不需要--
答案: 底层自动帮我们实现了--的操作.
2. 普通for : 可以删除, 但是索引需要--
3. 增强for : 不能删, 因为会产生并发修改异常
- 案例演示LinkedList集合的使用.
public static void main(String[] args) {
// 底层是链表结构
LinkedList<String> list = new LinkedList<>();
// 调用从Collection中继承下来的add方法添加元素
list.add("1");
list.add("2");
list.add("3");
// LinkedList特有的方法: 在头部添加元素
list.addFirst("4");
list.addFirst("5");
// LinkedList特有的方法: 在尾部添加元素
list.addLast("6");
// LinkedList特有的方法: 删除第一个元素
list.removeFirst();
// LinkedList特有的方法: 删除最后一个元素
list.removeLast();
System.out.println(list);
}
- 问题: LinkedList集合底层是链表结构, 那么查询元素的时候从前开始找, 还是从后开始找?
总结:
1. 会查看要查找的元素, 离头近还是离尾近, 如果离头近, 就从头开始找, 反之, 就从尾开始找.
2. LinkedList虽然是链表结构, 但是底层也有索引 !