Lison
<dreamlison@163.com>
, v1.0.0
, 2023.04.23
JAVA-编程基础-10-集合
文章目录
List、Set、Map、队列全面解析
Java 集合框架可以分为两条大的支线:"
- Collection,主要由 List、Set、Queue 组成,List 代表有序、可重复的集合,典型代表就是封装了动态数组的 ArrayList 和封装了链表的 LinkedList;Set 代表无序、不可重复的集合,典型代表就是 HashSet 和 TreeSet;Queue 代表队列,典型代表就是双端队列 ArrayDeque,以及优先级队列 PriorityQueue。
- Map,代表键值对的集合,典型代表就是 HashMap。
List
List 的特点是存取有序,可以存放重复的元素,可以用下标对元素进行操作。
ArrayList
java
// 创建一个集合
ArrayList<String> list = new ArrayList<String>();
// 添加元素
list.add("Li");
list.add("Cun");
list.add("Lison");
// 遍历集合 for 循环
for (int i = 0; i < list.size(); i++) {
String s = list.get(i);
System.out.println(s);
}
// 遍历集合 for each
for (String s : list) {
System.out.println(s);
}
// 删除元素
list.remove(1);
// 遍历集合
for (String s : list) {
System.out.println(s);
}
// 修改元素
list.set(1, "Lison520");
// 遍历集合
for (String s : list) {
System.out.println(s);
}
简单介绍一下 ArrayList 的特征
- ArrayList 是由数组实现的,支持随机存取,也就是可以通过下标直接存取元素;
- 从尾部插入和删除元素会比较快捷,从中间插入和删除元素会比较低效,因为涉及到数组元素的复制和移动;
- 如果内部数组的容量不足时会自动扩容,因此当元素非常庞大的时候,效率会比较低。
创建ArrayList
java
ArrayList<String> alist = new ArrayList<String>();
可以通过上面的语句来创建一个字符串类型的 ArrayList(通过尖括号来限定 ArrayList 中元素的类型,如果尝试添加其他类型的元素,将会产生编译错误),更简化的写法如下:
java
List<String> alist = new ArrayList<>();
由于 ArrayList 实现了 List 接口,所以 alist 变量的类型可以是 List 类型;new 关键字声明后的尖括号中可以不再指定元素的类型,因为编译器可以通过前面尖括号中的类型进行智能推断
此时会调用无参构造方法(见下面的代码)创建一个空的数组,常量DEFAULTCAPACITY_EMPTY_ELEMENTDATA的值为 {}
java
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
如果非常确定 ArrayList 中元素的个数,在创建的时候还可以指定初始大小。
java
List<String> alist = new ArrayList<>(20);
这样做的好处是,可以有效地避免在添加新的元素时进行不必要的扩容。
向ArrayList中添加元素
可以通过 add()
方法向 ArrayList 中添加一个元素:
java
alist.add("Lison");
java
堆栈过程图示:
add(element)
└── if (size == elementData.length) // 判断是否需要扩容
├── grow(minCapacity) // 扩容
│ └── newCapacity = oldCapacity + (oldCapacity >> 1) // 计算新的数组容量
│ └── Arrays.copyOf(elementData, newCapacity) // 创建新的数组
├── elementData[size++] = element; // 添加新元素
└── return true; // 添加成功
先是 add()
方法的源码
java
/** jdk1.8
* 将指定元素添加到 ArrayList 的末尾
* @param e 要添加的元素
* @return 添加成功返回 true
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 确保 ArrayList 能够容纳新的元素
elementData[size++] = e; // 在 ArrayList 的末尾添加指定元素
return true;
}
参数 e 为要添加的元素,此时的值为"Lison520",size 为 ArrayList 的长度,此时为 0。
java
/**
* 确保 ArrayList 能够容纳指定容量的元素
* @param minCapacity 指定容量的最小值
*/
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { // 如果 elementData 还是默认的空数组
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); // 使用 DEFAULT_CAPACITY 和指定容量的最小值中的较大值
}
ensureExplicitCapacity(minCapacity); // 确保容量能够容纳指定容量的元素
}
此时:
- 参数 minCapacity 为 1(size+1 传过来的)
- elementData 为存放 ArrayList 元素的底层数组,前面声明 ArrayList 的时候讲过了,此时为空
{}
- DEFAULTCAPACITY_EMPTY_ELEMENTDATA 前面也讲过了,为
{}
所以,if 条件此时为 true,if 语句minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity)
要执行。
DEFAULT_CAPACITY 为 10(见下面的代码),所以执行完这行代码后,minCapacity 为 10,Math.max()
方法的作用是取两个当中最大的那个。
java
private static final int DEFAULT_CAPACITY = 10;
执行 ensureExplicitCapacity()
方法,来看一下源码:
java
/**
* 检查并确保集合容量足够,如果需要则增加集合容量。
*
* @param minCapacity 所需最小容量
*/
private void ensureExplicitCapacity(int minCapacity) {
// 检查是否超出了数组范围,确保不会溢出
if (minCapacity - elementData.length > 0)
// 如果需要增加容量,则调用 grow 方法
grow(minCapacity);
}
此时:
- 参数 minCapacity 为 10
- elementData.length 为 0(数组为空)
所以 10-0>0,if 条件为 true,进入 if 语句执行 grow()
方法,来看源码:
java
/**
* 扩容 ArrayList 的方法,确保能够容纳指定容量的元素
* @param minCapacity 指定容量的最小值
*/
private void grow(int minCapacity) {
// 检查是否会导致溢出,oldCapacity 为当前数组长度
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); // 扩容至原来的1.5倍
if (newCapacity - minCapacity < 0) // 如果还是小于指定容量的最小值
newCapacity = minCapacity; // 直接扩容至指定容量的最小值
if (newCapacity - MAX_ARRAY_SIZE > 0) // 如果超出了数组的最大长度
newCapacity = hugeCapacity(minCapacity); // 扩容至数组的最大长度
// 将当前数组复制到一个新数组中,长度为 newCapacity
elementData = Arrays.copyOf(elementData, newCapacity);
}
此时:
- 参数 minCapacity 为 10
- 变量 oldCapacity 为 0
所以 newCapacity 也为 0,于是 newCapacity - minCapacity
等于 -10 小于 0,于是第一个 if 条件为 true,执行第一个 if 语句 newCapacity = minCapacity
,然后 newCapacity 为 10。
紧接着执行 elementData = Arrays.copyOf(elementData, newCapacity);
,也就是进行数组的第一次扩容,长度为 10。
回到 add()
方法:
java
public boolean add(E e) {
ensureCapacityInternal(size + 1);
elementData[size++] = e;
return true;
}
执行 elementData[size++] = e
。
此时:
- size 为 0
- e 为 "Lison520"