目录
[1.1 什么是 List](#1.1 什么是 List)
[1.2 常见接口介绍](#1.2 常见接口介绍)
[1.3 List的使用](#1.3 List的使用)
[2. ArrayList 与 顺序表](#2. ArrayList 与 顺序表)
[2.1 线性表](#2.1 线性表)
[2.2 顺序表](#2.2 顺序表)
[2.3 ArrayList简介](#2.3 ArrayList简介)
[2.4 ArrayList使用](#2.4 ArrayList使用)
[1. ArrayList的构造](#1. ArrayList的构造)
[1.1 ArrayList 基础属性的含义编辑](#1.1 ArrayList 基础属性的含义编辑)
[1.2 ArrayList() 解析](#1.2 ArrayList() 解析)
[1.3 ArrayList(int initialCapacity) 解析](#1.3 ArrayList(int initialCapacity) 解析)
[1.4 ArrayList(Collection c) 解析](#1.4 ArrayList(Collection c) 解析)
[2. ArrayList 的常见操作](#2. ArrayList 的常见操作)
[2.1 add(int index, E element)](#2.1 add(int index, E element))
[2.2 remove的区分](#2.2 remove的区分)
[2.3 subList 存在问题](#2.3 subList 存在问题)
[3. ArrayList的遍历](#3. ArrayList的遍历)
[4. ArrayList的扩容](#4. ArrayList的扩容)
[5.1 用于字符串](#5.1 用于字符串)
[5.2 杨辉三角:](#5.2 杨辉三角:)
[5.3 洗牌算法](#5.3 洗牌算法)
[2.5. ArrayList的缺点](#2.5. ArrayList的缺点)
1.List
1.1 什么是 List
在 集合框架 中,List 是一个接口,继承自 Collection

Collection 也是一个接口,该接口 规范 了后序容器中常用的一些方法, 具体如下:

Iterable 也是一个接口,表示实现该接口的类是可以 逐个元素遍历 的,具体如下:

但是站在 数据结构 的角度来看,List 就是一个 线性表。
即:n 个具有 相同类型元素 的 有限序列。
在该序列上可以执行 增删改查 以及 遍历 等操作。
1.2 常见接口介绍
List中提供了好多的方法,具体如下:

虽然方法比较多,但是常用方法如下:

增:add addAll
删:remove clear
改:set
查:get indexOf lastIndexOf
裁:subList
1.3 List的使用
注意:List是个接口,并不能直接用来实例化。
如果要使用,必须去实例化List的实现类。在集合框中,ArrayList 和 LinkedList 都实现了List接口。
2. ArrayList 与 顺序表
2.1 线性表
线性表 (linear list)是 n 个具有 相同特性 的数据元素的有序序列。
常见的线性表:顺序表、链表、栈、队列 ......
线性表在 逻辑 上是线性结构,也就说是 连续的一条直线 。但是在 物理结构 上并不一定是连续的,线性表在物理上存储时,通常以 数组和链式结构 的形式存储。

2.2 顺序表
1.什么是顺序表
顺序表是用一段 物理地址连续的存储单元 依次存储数据元素的线性结构

2.为什么要有顺序表?
数组本身自带的方法无法满足需求。
需要我们自己定义一个类,让类来提供方法,帮助操作数组。
3.自己实现一个add方法
画图:

代码:
java
public void add(int data) {
if (isFull()) {
grow();
}
this.array[this.usedSize] = data;
this.usedSize++;
}
private void grow() {
this.array = Arrays.copyOf(this.array, 2*this.array.length)
}
public boolean isFull() {
return this.usedSize == array.length;
}
逻辑非常严谨。
所以感觉很简单的功能,要写很多代码来保证严谨性。
2.3 ArrayList简介
在集合框架中,ArrayList是一个普通的类,实现了List接口,具体框架图如下:

【说明】
泛型:
ArrayList是以泛型方式实现的,使用时必须要先实例化
接口:
ArrayList实现了RandomAccess接口,表明ArrayList支持随机访问
ArrayList实现了Cloneable接口,表明ArrayList是可以clone的
ArrayList实现了Serializable接口,表明ArrayList是支持序列化的
并发:
和Vector不同,ArrayList不是线程安全的,在单线程下可以使用,在多线程中可以选择Vector或者CopyOnWriteArrayList
底层:
ArrayList底层是一段连续的空间,并且可以动态扩容,是一个动态类型的顺序表
2.4 ArrayList使用

1. ArrayList的构造

java
public static void main(String[] args) {
// ArrayList创建
// 构造一个空的列表
List<Integer> list1 = new ArrayList<>();
// 构造一个具有10个容量的列表
List<Integer> list2 = new ArrayList<>(10);
list2.add(1);
list2.add(2);
list2.add(3);
// 利用 list2 构造 list3
List<Integer> list3 = new ArrayList<>(list2);
}
1.1 ArrayList 基础属性的含义
1.2 ArrayList() 解析
此处涉及到一部分扩容,仔细理解,扩容剩余部分在扩容部分进行讲解.

1.3 ArrayList(int initialCapacity) 解析

1.4 ArrayList(Collection<? extends E> c) 解析

2. ArrayList 的常见操作

2.1 add(int index, E element)
2.2 remove的区分

此处红线原因:
因为这个操作过时了,但是可以用就是用的很少。
2.3 subList 存在问题

java
public static void main(String[] args) {
ArrayList<Integer> test = new ArrayList<>();
test.add(1);
test.add(2);
test.add(3);
test.add(4);
test.add(5);
System.out.println(test);
List<Integer> list = test.subList(1, 3);
System.out.println(list);
}
[1,3)
输出结果:
此时对裁剪后的数组进行改变,会出现与预期不符合的效果.
java
public static void main3(String[] args) {
ArrayList<Integer> test = new ArrayList<>();
test.add(1);
test.add(2);
test.add(3);
test.add(4);
test.add(5);
System.out.println(test);
List<Integer> list = test.subList(1, 3);
System.out.println(list);
System.out.println("======================================");
list.set(0, 99);
System.out.println(test); // 预期 1 2 3 4 5
System.out.println(list); // 预期 99 3
}
结果:

原因(如下图表示和代码分析):直接引用并没有创建新的对象


3. ArrayList的遍历
下面代码介绍了 ArrayList 的四种遍历方式
java
public static void main(String[] args) {
ArrayList<Integer> test = new ArrayList<>();
test.add(1);
test.add(2);
test.add(3);
test.add(4);
test.add(5);
System.out.println(test);
System.out.println("===使用迭代器输出 listIterator 拓展功能===");
ListIterator<Integer> iterator3 = test.listIterator(test.size());
while (iterator3.hasPrevious()) {
System.out.print(iterator3.previous() + " ");
}
System.out.println();
System.out.println("===使用迭代器输出 iterator===");
Iterator<Integer> iterator = test.iterator();
while (iterator.hasNext()) {
System.out.print(iterator.next() + " ");
}
System.out.println();
System.out.println("===使用迭代器输出 listIterator===");
ListIterator<Integer> iterator2 = test.listIterator();
while (iterator2.hasNext()) {
System.out.print(iterator2.next() + " ");
}
System.out.println();
int size = test.size();
System.out.println("===for循环输出===");
for (int i = 0; i < size; i++) {
System.out.print(test.get(i) + " ");
}
System.out.println();
System.out.println("===for each循环输出===");
for (Integer x : test) {
System.out.print(x + " ");
}
System.out.println();
}
4. ArrayList的扩容
总结:
检测是否真正需要扩容,如果是调用grow准备扩容
预估需要库容的大小初步预估按照1.5倍大小扩容如果用户所需大小超过预估1.5倍大小,则按照用户所需大小扩容,真正扩容之前检测是否能扩容成功,防止太大导致扩容失败
使用copyOf进行扩容
具体解析如下图:

5.ArrayList的练习
5.1 用于字符串

代码:

为什么 str2.contains(ch + "") 要 + ""
因为contains的参数是 CharSequence 而不是 Character

5.2 杨辉三角:

java
public List<List<Integer>> generate(int numRows) {
List<List<Integer>> ret = new ArrayList<>();
List<Integer> list0 = new ArrayList<>();
list0.add(1);
ret.add(list0);
// 从第二行开始 进行求每个元素
for (int i = 1; i < numRows; i++) {
// 处理第一个元素
List<Integer> curRow = new ArrayList<>();
curRow.add(1);
// 中间
List<Integer> preRow = new ArrayList<>();
preRow = ret.get(i - 1);
for (int j = 1; j < i; j++) {
int val1 = preRow.get(j);
int val2 = preRow.get(j - 1);
curRow.add(val1 + val2);
}
// 尾巴
curRow.add(1);
ret.add(curRow);
}
5.3 洗牌算法
买牌:
java
public List<Cart> buyCard() {
List<Cart> cartList = new ArrayList<>();
for (int i = 1; i <= 13; i++) {
for (int j = 0; j < 4; j++) {
int rank = i;
String suit = suits[j];
Cart cart = new Cart(suit, rank);
cartList.add(cart);
}
}
return cartList;
}
洗牌:
java
public void shuffle(List<Cart> cartList) {
Random random = new Random();
for (int i = cartList.size() - 1; i > 0; i--) {
int index = random.nextInt(cartList.size());
swap(cartList, i, index);
}
}
private void swap(List<Cart> cartList, int i, int j) {
Cart tmp = cartList.get(i);
cartList.set(i, cartList.get(j));
cartList.set(j, tmp);
}
玩牌:
java
public List<List<Cart>> play(List<Cart> cartList) {
List<Cart> hand0 = new ArrayList<>();
List<Cart> hand1 = new ArrayList<>();
List<Cart> hand2 = new ArrayList<>();
List<List<Cart>> hand = new ArrayList<>();
hand.add(hand0);
hand.add(hand1);
hand.add(hand2);
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 3; j++) {
Cart cart = cartList.remove(0);
hand.get(j).add(cart);
}
}
return hand;
}
2.5. ArrayList的缺点
-
ArrayList底层使用连续的空间,任意位置插入或删除元素时,需要将该位置后序元素整体往前或者往后搬移,故时间复杂度为O(N)
-
扩容 需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。
-
扩容一般是呈 1.5 倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。