- 继承 Collection 接口,它是一个元素存取 有序 的集合
例如,存元素的顺序是11、22、33。那么集合中,元素的存储就是按照11、22、33的顺序完成的)
-
它是一个 带有索引 的集合,通过索引就可以精确的操作集合中的元素(与数组的索引是一个道理)
-
与 set 不同,List 中 可以有重复的元素,通过元素的
equals
方法,来比较是否为重复的元素 -
List 接口的常用实现类有:
-
ArrayList 集合
-
LinkedList 集合
2. List 接口中的抽象方法(特有)
add(int index, Object e)
: 向集合指定索引处,添加指定的元素,原元素依次 后移remove(int index)
:将指定索引处的元素,从集合中删除,返回值为被删除的元素set(int index,E element)
:将指定索引处的元素,替换成指定的元素,返回值为替换前的元素get(int index)
:获取指定索引处的元素,并返回该元素
3. List 遍历
List a = new LinkedList<>();
a.add(new Person(“zs”,10));
a.add(new Person(“lisi”,20));
a.add(new Person(“wangwu”,30));
//利用Iterator来遍历List
Iterator iterator = a.iterator();
//利用迭代器对象遍历
while(listItr.hasNext()) {
System.out.println(listItr.next());
}
//List独有的遍历方式
for (int i = 0; i < a.size(); i++) {
System.out.println(a.get(i));
}
二、ListIterator 接口
1.概述
- List 不仅有自己独有的迭代方式,还有自己独有的迭代器:
ListIterator
2. ListIterator 接口的抽象方法
add(E, e)
:将指定元素插入列表boolean hasPrevious()
:逆向遍历列表,若列表迭代器有多个元素,则返回 true,也就是判断是否有前一个元素previous()
:返回列表的前一个元素
3. List 逆向遍历:
Iterator iterator = a.iterator();
ListIterator listItr = a.listIterator(a.size());
//先顺序遍历,让 cursor 到最后
while(listItr.hasNext()) {
System.out.println(listItr.next());
}
//逆向遍历
//先previous向前移动一个位置,再访问cursor指向的元素
while(listItr.hasPrevious()) {
System.out.println(listItr.previous());
}
三、迭代器的并发修改异常
1. 迭代器的并发修改异常
java.util.ConcurrentModificationException
- 就是在遍历的过程中,使用了集合方法 修改了 集合的长度
2. 出现场景:
首先,在遍历集合的过程中修改集合;其次,修改集合行为,不是迭代器对象来完成的,而是直接修改 Collection 对象
//场景实现
List list = new ArrayList();
list.add(“abc1”);
list.add(“abc2”);
list.add(“abc3”);
list.add(“abc4”);
//对集合使用迭代器进行获取,获取时候判断集合中是否存在 “abc3"对象
//如果有,添加一个元素 “ABC3”
Iterator it = list.iterator();
while(it.hasNext()){
String s = it.next();
//对获取出的元素s,进行判断,是不是有"abc3”
if(s.equals(“abc3”)){
list.add(“ABC3”);
}
System.out.println(s);
}
3. 原因:
在迭代过程中,使用了集合的方法对元素进行操作。导致迭代器并不知道集合中的变化,容易引发数据的不确定性。迭代器对象,是依赖与当前的数据集合产生的(换言之,迭代器依赖于数据集,它们必须对应)
public class ListDemo{
public static void main(String[] args) {
List a = new LinkedList<>();
//迭代器
ListIterator listItr = a.listIterator();
a.add(new Person(“zs”,10));
a.add(new Person(“lisi”,20));
a.add(new Person(“wangwu”,30));
while(listItr.hasNext()){
Person p = listItr.next();
//这种添加元素的方式,会产生异常
//a.add(new Person(“zhaoliu”, 40));
//解决: 利用ListIterator对象添加元素
listItr.add(new Person(“zhaoliu”, 40));
}
}
System.out.println(s);
//针对List还有另外一种,在遍历集合同时修改集合的解决方案
for (int i = 0; i < a.size(); i++){
if(“lisi”.equals(a.get(i).name)){
a.add(new Person(“zhaoliu”, 40));
}
}
System.out.println(a);
//如果使用 ListIterator 的add方法向集合中添加元素,这个元素的位置处在当前遍历到的元素之后的位置
//如果使用 集合对象的 add(e) 方法添加元素,插入的元素处在表尾位置
}
四、ArrayList 、LinkedList 集合
1. ArrayList 集合
- 底层采用的是 数组 结构,线程不安全,查询快,增删慢
//创建了一个长度为0的Object类型数组
ArrayList al=new ArrayList();
al.add(“abc”);
//本质:
//底层会创建一个长度为10的Object数组 Object[] obj=new Object[10]
//obj[0]=“abc”
//如果添加的元素的超过10个,底层会开辟一个1.5*10的长度的新数组
//把原数组中的元素拷贝到新数组,再把最后一个元素添加到新数组中
2. LinkedList 集合
- 底层采用 链表 结构,线程不安全,查询慢,增删快
- 每次查询都要从链头或链尾找起,查询相对数组较慢,但是删除直接修改元素记录的地址值即可,不要大量移动元素
- LinkedList 的索引决定是从链头开始找还是从链尾开始找,如果该元素小于元素长度一半,从链头开始找起,如果大于元素长度的一半,则从链尾找起
- LinkedList 提供了大量的操作开始和结尾的方法
- 子类的特有功能:不能多态调用:
addFirst(E) 添加到链表的开头
addLast(E) 添加到链表的结尾
E getFirst() 获取链表的开头
E getLast() 获取链表的结尾
E removeFirst() 移除并返回链表的开头
E removeLast() 移除并返回链表的结尾
3. Vector 集合(基本不用)
- Vector 集合数据存储的结构是 数组 结构,为JDK中最早提供的集合,它是线程同步的,线程安全的
- Vector 集合已被 ArrayList 替代
五、Set 接口
1. 特点
-
它是个 不包含重复元素 的集合,没索引
-
是一个不包含重复元素的 collection
-
无序集合,没有索引,不存储重复元素
-
Set无序:存储和取出的顺序不同,
-
Set集合取出元素的方式可以采用:迭代器、增强for
-
代码的编写上,和 ArrayList 完全一致
-
Set集合常用实现类:
-
HashSet 集合
-
LinkedHashSet 集合
六、 HashSet (哈希表)
1. 特点:
- 底层数据结构为 哈希表
- 存储、取出都比较快
- 线程不安全,运行速度快
- 不保证 set 的迭代顺序
- 不保证该顺序的恒久不变
2. 哈希表的数据结构:
- 加载因子:表中填入的记录数/哈希表的长度
例如:加载因子是 0.75 代表:数组中的16个位置, 其中存入 16 * 0.75 = 12个元素。 - 如果在存入第十三个( > 12 )元素,导致存储链子过长,会降低哈希表的性能,那么此时会扩充哈希表(再哈希),底层会开辟一个长度为原长度2倍的数组,把老元素拷贝到新数组中,再把新元素添加数组中。
当存入元素数量 > 哈希表长度 * 加载因子
,就要扩容,因此加载因子决定扩容时机
3. 字符串对象的哈希值:
- 对象的哈希值,是普通的十进制整数,Object 类的方法
public int hashCode()
来计算,计算结果 int 整数 - String 类重写了
hashCode()
方法,见源码
4. 哈希表的存储过程
存取原理:
每存入一个新的元素都要走以下三步:
1. 首先调用本类的hashCode()
方法算出哈希值
2. 在容器中找是否与新元素哈希值相同的老元素,如果没有直接存入,如果有转到第三步
3. 新元素会与该索引位置下的老元素利用equals 方法
一一对比,一旦新元素.equals(老元素)
,返回 true,停止对比,说明重复,不再存入,如果与该索引位置下的老元素都通过 equals 方法对比返回 false,说明没有重复,存入
哈希表的存储过程
5. 哈希表的存储自定义对象
- 自定义对象需要重写 hashCode() 和 equals(),来保证存入对象的不重复
//重写hachCode() 方法
public int hashCode(){
return name.hashCode() +age +id; //算法为name的hashCode值+age+id
}
//重写equals
public boolean equals(Object obj){
if(this == obj)
return true;
if(obj == null)
return false;
if(obj instanceof Student){
Student s = (Student)obj;
return name.equals(s.name) && age == s.age && id == s.id;
}
return false;
}
6. LinkedHashSet 集合
-
LinkedHashSet
基于链表的哈希表实现,继承自HashSet
-
LinkedHashSet 自身特性:
-
具有顺序,存储和取出的顺序相同的,线程不安全,运行速度块
public class LinkedHashSetDemo {
public static void main(String[] args) {
LinkedHashSet link = new LinkedHashSet();
link.add(123);
link.add(44);
link.add(33);
link.add(33);
System.out.println(link);
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Java开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
1715709422933)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Java开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!