Java-进阶:集合框架2,springboot源码分析视频

3. Vector 集合(基本不用)
五、Set 接口
六、 HashSet (哈希表)
1. 特点
2. 哈希表的数据结构
3. 字符串对象的哈希值
4. 哈希表的存储过程
5. 哈希表的存储自定义对象
6. LinkedHashSet 集合
7. ArrayList,HashSet 判断对象是否重复的原因
七、TreeSet
1.概述
2. TreeSet如何实现,不能存储重复元素
3. 向TreeSet中放入,自定义的类的对象
4. 注意事项


一、List 接口

1. 概述

  • 继承 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()

《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》

【docs.qq.com/doc/DSmxTbFJ1cmN1R2dB】 完整内容开源分享

来计算,计算结果 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 集合

e+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 集合

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值