1.概念
1.1.集合与数组的区别
集合:长度不固定,动态的根据数据添加删除改变长度,并且只能存入引用类型,读取采用迭代器或其他方法
数组:长度固定,不可改变,既可以存入基本类型也可以存入引用类型,读取使用索引读(for)
|----|-----------------------|--------------------|------------|
| | 长度 | 存入类型 | 读取 |
| 集合 | 长度不固定,动态的根据数据添加删除改变长度 | 只能存入引用类型 | 采用迭代器或其他方法 |
| 数组 | 长度固定,不可改变 | 既可以存入基本类型也可以存入引用类型 | 使用索引(for) |
1.2.集合分类
分为三类:List类,Set类,Map类
List集合:集合里面元素有序,并且允许可重复
Set集合:集合里面元素无序,并且不可重复(保证唯一性)
Map集合:集合采用键值对方式,key唯一(不允许重复)无序,value没有要求
|------|------|-------|
| | 是否有序 | 是否可重复 |
| List | 有序 | 可重复 |
| Set | 无序 | 不可重复 |
1.3.Collection和Collections的区别
Collection是一个接口,给集合实现的,里面定义了一些操作集合的方法
Collections是一个工具类,位于java.util包中,可以直接使用该类操作集合(增删改,排序)
1.4.集合遍历的方法
有六种方法:for,增强for,迭代器,列表迭代器,foeEach,Stream流
**for:**带索引查询(区分集合是否带索引,才能使用该方法)
java
List<String> list = Arrays.asList("A", "B", "C");
// 通过索引遍历(适合 ArrayList 等支持随机访问的集合)
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
增强for:没有索引,直接遍历查询
java
List<String> list = Arrays.asList("A", "B", "C");
// 直接遍历元素(底层基于迭代器实现)
for (String item : list) {
System.out.println(item);
}
**迭代器:**在迭代器里面只能删除元素,不能插入元素
java
List<String> list = Arrays.asList("A", "B", "C");
Iterator<String> iterator = list.iterator();
// 通过迭代器遍历(适用于所有 Collection)
while (iterator.hasNext()) {
String item = iterator.next();
System.out.println(item);
// 可在遍历中安全删除元素:iterator.remove();
}
java
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
if ("B".equals(item)) {
iterator.remove(); // 允许删除当前元素
// iterator.add("D"); // 编译错误:Iterator 没有 add() 方法
}
}
列表迭代器:没有限制,可以进行删除查询插入元素
java
List<String> list = Arrays.asList("A", "B", "C");
ListIterator<String> listIterator = list.listIterator();
// 正向遍历(从头到尾)
while (listIterator.hasNext()) {
String item = listIterator.next();
System.out.println(item);
}
// 反向遍历(从尾到头)
while (listIterator.hasPrevious()) {
String item = listIterator.previous();
System.out.println(item);
}
java
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
ListIterator<String> listIterator = list.listIterator();
// 正向遍历
while (listIterator.hasNext()) {
String item = listIterator.next();
if ("B".equals(item)) {
listIterator.remove(); // 删除当前元素
listIterator.add("D"); // 在当前位置插入新元素
listIterator.set("E"); // 替换当前元素(需在 next() 或 previous() 后调用)
}
}
// 反向遍历
while (listIterator.hasPrevious()) {
String item = listIterator.previous();
System.out.println(item);
}
forEach:因为它基于迭代器实现的,因此也不能在循环中插入元素
java
List<String> list = Arrays.asList("A", "B", "C");
// 使用 Lambda 表达式遍历
list.forEach(item -> System.out.println(item));
// 或使用方法引用
list.forEach(System.out::println);
Stream:没有限制
java
List<String> list = Arrays.asList("A", "B", "C");
// 转换为 Stream 并遍历
list.stream().forEach(item -> System.out.println(item));
// 并行流遍历(多线程处理)
list.parallelStream().forEach(item -> System.out.println(item));
2.List
2.1.List的实现
实现List的集合有:ArrayList,LinkedList,Vector
ArrayList:基于动态的数组创建的,查询效率高,增删效率一般,线程不安全
LinkedList:基于双向链表创建的,查询效率一般,增删效率高,线程不安全
Vector:基于动态数组创建的,与ArrayList类似,不过它是线程安全的
|------------|------|-----|-----|------|
| | 数据结构 | 读操作 | 写操作 | 线程安全 |
| ArrayList | 数组 | 高 | 一般 | 不安全 |
| LinkedList | 双向链表 | 一般 | 高 | 不安全 |
| Vector | 数组 | 高 | 一般 | 安全 |
2.2.可以一边遍历一边修改List的方法
首先思考有几个遍历方法:六个
哪些是不能修改元素的:迭代器,forEach
最终得到的方法:for,增强for,列表迭代器,Stream流
2.3.List快速删除元素的原理
原理是基于集合底层数据结构不同,分为两类**:ArrayList,LinkedList**
ArrayList:基于数组对吧,原先数组是通过索引删除数据,那么因此ArrayList也是如此,基于索引来删除数据
具体实现:如果你是删除尾部最后一个数据,直接删除即可,时间复杂度为O(1),如果不是,那么它会将索引元素删除后,将后面的元素往前面覆盖,然后计算出集合长度,时间复杂度为O(n),n为元素的个数
LinkedList:基于双向链表,简单来说链表由节点组成,每个节点包含自己的数据与前一个节点的引用和后一个节点的引用,实现双向并通
具体实现:如果你是删除尾部最后一个数据,直接删除即可,时间复杂度为O(1),如果不是,那么就是从头或尾进行查询删除,时间复杂度O(n)
2.4.ArrayList与LinkedList的区别
- 数据结构组成不同:Array List基于数组,LinkedList基于双向链表
- 删除和插入效率不同:ArrayList在尾部的效率高(平均O(1)),在其他的地方效率低,由于需要进行元素覆盖,而LinkedList它基于链表引用,在尾部的效率(O(1)比ArrayList效率低(ArrayList基于数组,内存是连续的,而LinkedList基于链表,内存不连续),在其他地方删除与插入与ArrayList效率差不多(O(n))
- 随机访问速度:由于ArrayList基于数组根据索引查询,时间复杂度O(1),而LinkedList基于链表,它需要从头或尾部访问,因此时间复杂度为O(n)
- 适用场景不同:ArrayList更适合高频的随机访问操作或尾部插入为主,LinkedList更适合高频头尾插入/删除(队列)或需要双向遍历
- 线程安全:都是线程不安全的
2.5.线程安全
实现线程(List)安全的方法有:
- 实现Collections.synchronizedList,将线程不安全的List集合加个锁,变成安全的
- 直接使用线程安全的List集合:比如Vector,CopyOnWirteArrayList
2.6.ArrayList的扩容机制
首先如果你没有指定长度,默认长度为10,当你要添加元素并且超过此时容量长度时,就会进行扩容操作
实现:
1.扩容:创建一个新的数组,新数组的长度为原数组的1.5倍数,然后再检查容量是否足够,不够继续扩容
2.复制:将旧的数组里面的值复制进新的数组中,再进行写操作
3.更改引用:将原先指向旧数组的引用指向新数组
4.扩容完成:可以继续扩容
2.7.CopyOnWirteArrayList
它实现了读写分离,写操作加了互斥锁ReentrantLock,避免出现线程安全问题,而读操作没有加锁,使用volatile关键字修饰数组,保证当前线程对数组对象重新赋值后,其他线程可以及时感知到(所有线程可见性)。线程读取数据可以直接读取,提高效率
写操作:它不会向ArrayList一样直接扩容1.5倍,它是根据你的添加元素个数多少来扩容,如果你只添加一个元素,那么它会创建一个新数组,长度比旧数组长度多一,然后依旧是依次复制元素进新数组中,改变内部引用指向(需要频繁创建新的数组,以时间换空间)
读操作:就是说它不会管你的数据是否修改,内部指向是旧数组,那么就读取旧数组的数据,指向是新数组就读取新数据,这样效率会高(数据弱一致性)