1.Collection集合
1.1数组和集合的区别【理解】
-
相同点
都是容器,可以存储多个数据
-
不同点
-
数组的长度是不可变的,集合的长度是可变的
-
数组可以存基本数据类型和引用数据类型
集合只能存引用数据类型,如果要存基本数据类型,需要存对应的包装类
-
1.2集合类体系结构【理解】
1.3Collection 集合概述和使用【应用】
-
Collection集合概述
- 是单例集合的顶层接口,它表示一组对象,这些对象也称为Collection的元素
- JDK 不提供此接口的任何直接实现.它提供更具体的子接口(如Set和List)实现
-
创建Collection集合的对象
- 多态的方式
- 具体的实现类ArrayList
-
Collection集合常用方法
boolean addAll(Collection<? extends E> c)代表一次添加多个元素,参数就是集合
public class Main { public static void main(String[] args) { //目的: 为了学习Collection接口里面的方法 //自己在做一些练习的时候,还是按照之前的方式去创建对象。 Collection<Integer> coll = new ArrayList<>(); //1.添加元素 //细节1: 如果我们要往List系列集合中添加数据,那么方法永远返回true,因为List系列的是允许元素重复的 //细节2:如果我们要往Set系列集合中添加数据,如果当前要添加元素不存在,方法返回true,表示添加成功。 //如果当前要添加的元素已经存在,方法返回false,表示添加失败。 //因为Set系列的集合不允许重复。 coll.add(1); coll.add(2); coll.add(3); System.out.println(coll);//[1, 2,3] //2.删除元素 //细节1:因为Collection里面定义的是共性的方法,所以此时不能通过索引进行删除。只能通过元素的对象进行删除 //细节2: 方法会有一个布尔类型的返回值,删除成功返回true,删除失败返回false //如果要删除的元素不存在,就会删除失败。 System.out.println(coll.remove(2)); System.out.println(coll);//true System.out.println(coll.remove(10)); System.out.println(coll);//false //3.判断元素是否包含 //细节: 底层是依赖equals方法进行判断是否存在的。 //所以,如果集合中存储的是自定义对象,也想通过contains方法来判断是否包含,那么在javabean类中,一定要重写equals方法. System.out.println(coll.contains(1));//true System.out.println(coll.contains(10));//false //4.判断集合是否为空 System.out.println(coll.isEmpty());//false //6.获取集合长度 System.out.println(coll);//[1, 3] System.out.println(coll.size());//2] //7.清空集合 coll.clear(); System.out.println(coll);//[] } }
1.4Collection集合的遍历
1.4.1 迭代器遍历
-
迭代器介绍
- 迭代器,集合的专用遍历方式
- Iterator iterator(): 返回此集合中元素的迭代器,通过集合对象的iterator()方法得到
-
Iterator中的常用方法
boolean hasNext(): 判断当前位置是否有元素可以被取出
E next(): 获取当前位置的元素,将迭代器对象移向下一个索引位置 -
Collection集合的遍历
public class Main { public static void main(String[] args) { Collection<Integer> coll = new ArrayList<>(); coll.add(1); coll.add(2); coll.add(3); coll.add(4); coll.add(5); coll.add(6); //获取迭代器对象 Iterator<Integer> iterator = coll.iterator(); //循环遍历 while (iterator.hasNext()){ int num = iterator.next(); System.out.println(num); } } }
注意事项
- 报错NoSuchElementException
- 迭代器遍历完毕,指针不会复位如果要遍历第二次,则需要重新获取迭代器对象
- 循环中只能用一次next方法,使用多次后,会出现跳跃取值和越界
- 迭代器遍历时,不能用集合的方法进行增加或者删除
如果实在要删除,就要通过迭代器中的remove方法删除,如果要添加,暂时没有办法
-
迭代器中删除的方法
void remove(): 删除迭代器对象当前指向的元素
public class IteratorDemo2 { public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); list.add("a"); list.add("b"); list.add("b"); list.add("c"); list.add("d"); Iterator<String> it = list.iterator(); while(it.hasNext()){ String s = it.next(); if("b".equals(s)){ //指向谁,那么此时就删除谁. it.remove(); } } System.out.println(list); } }
1.4.2 增强for
-
代码
public class MyCollectonDemo1 { public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); list.add("a"); list.add("b"); list.add("c"); list.add("d"); list.add("e"); list.add("f"); //1,数据类型一定是集合或者数组中元素的类型 //2,str仅仅是一个变量名而已,在循环的过程中,依次表示集合或者数组中的每一个元素 //3,list就是要遍历的集合或者数组 for(String str : list){ System.out.println(str); } } }
1.4.3 lambda表达式
利用forEach方法,再结合lambda表达式的方式进行遍历
public class A07_CollectionDemo7 {
public static void main(String[] args) {
/*
lambda表达式遍历:
default void forEach(Consumer<? super T> action):
*/
//1.创建集合并添加元素
Collection<String> coll = new ArrayList<>();
coll.add("zhangsan");
coll.add("lisi");
coll.add("wangwu");
//2.利用匿名内部类的形式
//底层原理:
//其实也会自己遍历集合,依次得到每一个元素
//把得到的每一个元素,传递给下面的accept方法
//s依次表示集合中的每一个数据
/* coll.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});*/
//lambda表达式
coll.forEach(s -> System.out.println(s));
}
}
2.List集合
2.1List集合的概述和特点【记忆】
- List集合的概述
- 有序集合,这里的有序指的是存取顺序
- 用户可以精确控制列表中每个元素的插入位置,用户可以通过整数索引访问元素,并搜索列表中的元素
- 与Set集合不同,列表通常允许重复的元素
- List集合的特点
- 存取有序
- 可以重复
- 有索引
2.2List集合的特有方法【应用】
- 方法介绍
- 示例代码
public class Main {
public static void main(String[] args) {
//List系列集合中的两个删除的方法
//1.直接删除元素
//2.通过索引进行删除
//1.创建集合并添加元素
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
//2.删除元素
//请问:此时删除的是1这个元素,还是1索引上的元素?
list.remove(1);
System.out.println(list);//[1, 3]
//为什么?
//因为在调用方法的时候,如果方法出现了重载现象
//优先调用,实参跟形参类型一致的那个方法。
//如果非要删除指定元素,则需要把int改为Integer类型
//手动装箱,手动把基本数据类型的1,变成Integer类型
Integer i = Integer.valueOf(1);
list.remove(i);
System.out.println(list);//[3]
//3.修改元素
int result = list.set(0,123);
System.out.println(list);//[123]
System.out.println(result);//3
//4.get方法,通过索引
System.out.println(list.get(0));//123
}
}
2.3List集合的五种遍历方式【应用】
- 迭代器
- 列表迭代器
- 增强for
- Lambda表达式
- 普通for循环
代码示例:
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
//1.迭代器
Iterator<Integer> it = list.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
//2.列表迭代器
ListIterator<Integer> ilt = list.listIterator();
while (ilt.hasNext()){
System.out.println(ilt.next());
}
//列表迭代器可以在遍历过程中添加和删除方法
System.out.println(list);//[1, 2, 3, 4]
ListIterator<Integer> ilt1 = list.listIterator();
while (ilt1.hasNext()){
if(ilt1.next()==3){
ilt1.add(33);
}
if(ilt1.next()==4){
ilt1.remove();
}
}
System.out.println(list);//[1, 2, 3, 33]
//3.增强for
for (Integer i : list) {
System.out.println(i);
}
//4.Lambda表达式
list.forEach(new Consumer<Integer>(){
@Override
public void accept(Integer i){
System.out.println(i);
}
});
list.forEach(i-> System.out.println(i));
//5.普通for循环
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
列表迭代器细节
右边两个方法不常用,相当于往前移动,不常用的原因是因为,迭代器初始指向了第一个元素,而不是最后一个元素。
3.数据结构
3.1数据结构之栈和队列【记忆】
-
栈结构
先进后出
-
队列结构
先进先出
3.2数据结构之数组和链表【记忆】
-
数组结构
-
链表结构
4.List集合的实现类
4.1List集合子类的特点【记忆】
-
ArrayList集合
底层是数组结构实现,查询快、增删慢
-
LinkedList集合
底层是链表结构实现,查询慢、增删快
4.2LinkedList集合的特有功能【应用】
-
特有方法
-
示例代码
LinkedList<Integer> list = new LinkedList<Integer>(); LinkedList<Integer> list1 = new LinkedList<Integer>(); //1.在开头插入指定元素 list.addFirst(1); list.addFirst(2); list.addFirst(3); System.out.println(list);//[3, 2, 1] //list.addFirst(num)等价于list.add(0,num); list1.add(0,1); list1.add(0,2); list1.add(0,3); System.out.println(list1);//[3, 2, 1] //2.在末尾插入指定元素 list.addLast(4); System.out.println(list);//[3, 2, 1, 4] //list.addLast(num)等价于list.add(list.size(),num); list1.add(list1.size(),4); System.out.println(list1);//[3, 2, 1, 4] //3.获取列表中第一个元素 System.out.println(list.getFirst());//3 //list.getFirst()等价于list.get(0) System.out.println(list1.get(0));//3 //4.获取列表中最后一个元素 System.out.println(list.getLast());//4 //list.getFirst()等价于list.get(list.size()-1) System.out.println(list.get(list.size()-1));//4 //5.从此列表中删除并返回第一个元素 System.out.println(list.removeFirst());//3 System.out.println(list);//[2, 1, 4] //list.removeFirst()等价于list.remove(0) System.out.println(list1.remove(0));//3 System.out.println(list1);//[2, 1, 4] //6.从此列表中删除并返回最后一个元素 System.out.println(list.removeLast());//4 System.out.println(list);//[2, 1] //list.removeLast()等价于list.remove(list.size()-1) System.out.println(list1.remove(list1.size()-1));//4 System.out.println(list1);//[2, 1]
5. 源码分析
5.1 ArrayList源码分析:
核心步骤:
-
创建ArrayList对象的时候,他在底层先创建了一个长度为0的数组。
数组名字:elementDate,定义变量size。
size这个变量有两层含义:
①:元素的个数,也就是集合的长度
②:下一个元素的存入位置 -
添加元素,添加完毕后,size++
扩容时机一:
-
当存满时候,会创建一个新的数组,新数组的长度,是原来的1.5倍,也就是长度为15.再把所有的元素,全拷贝到新数组中。如果继续添加数据,这个长度为15的数组也满了,那么下次还会继续扩容,还是1.5倍。
扩容时机二:
-
一次性添加多个数据,扩容1.5倍不够,怎么办呀?
如果一次添加多个元素,1.5倍放不下,那么新创建数组的长度以实际为准。
举个例子:
在一开始,如果默认的长度为10的数组已经装满了,在装满的情况下,我一次性要添加100个数据很显然,10扩容1.5倍,变成15,还是不够,
怎么办?
此时新数组的长度,就以实际情况为准,就是110
源码分析
- 第一次添加数据:
- 添加第11个元素:
5.2 LinkedList源码分析:
底层是双向链表结构
核心步骤如下:
- 刚开始创建的时候,底层创建了两个变量:一个记录头结点first,一个记录尾结点last,默认为null
- 添加第一个元素时,底层创建一个结点对象,first和last都记录这个结点的地址值
- 添加第二个元素时,底层创建一个结点对象,第一个结点会记录第二个结点的地址值,last会记录新结点的地址值
具体分析过程可以参见视频讲解。
5.3 迭代器源码分析:
迭代器遍历相关的三个方法:
-
Iterator iterator() :获取一个迭代器对象
-
boolean hasNext() :判断当前指向的位置是否有元素
-
E next() :获取当前指向的元素并移动指针