愿每个人都能遵循自己的时钟,做不后悔的选择

大家好,这里是新一,请多关照🙈🙉🙊。在本篇博客中,新一将会为大家介绍JAVA数据结构 - List源码分析及模拟实现,干货满满哟。(以下结果均在IDEA中编译)希望在方便自己复习的同时也能帮助到大家。😜😜😜🥇🥈🥉
废话不多说,直接进入我们的文章。
一.🌕 List的使用

👇👇👇
List官方文档戳这里
Array官方文档戳这里
LinkedList官方文档戳这里

public static void main(String[] args) {
//初始化
List<String> list1 = new ArrayList<>(20);
ArrayList<String> list2 = new ArrayList<>();
//添加元素
list2.add("hello");
list2.add("bit");
list2.add("world");
System.out.println(list2);
System.out.println("========");
//指定位置添加元素
list2.add(0,"a");
list2.add(4,"g");
System.out.println(list2);
System.out.println("========");
//使用另外一个ArrayList对list3进行初始化,类型必须相同
ArrayList<String> list3 = new ArrayList<>(list2);
System.out.println(list3);
System.out.println("========");
//list3中尾插list2中的所有元素
list3.addAll(list2);
System.out.println(list3);
}

public static void main(String[] args){
//初始化
ArrayList<String> list2 = new ArrayList<>();
//添加元素
list2.add("hello");
list2.add("bit");
list2.add("world");
System.out.println(list2);
System.out.println("========");
//remove指定位置元素
list2.remove(0);
list2.remove(1);
System.out.println(list2);
System.out.println("========");
//remove出现的第一个关键字
list2.add("a");
list2.add("bit");
list2.remove("bit");
System.out.println(list2);
System.out.println("========");
//clear 整个list
list2.clear();
System.out.println(list2);
}

public static void main(String[] args) {
//初始化
ArrayList<String> list2 = new ArrayList<>();
//添加元素
list2.add("hello");
list2.add("bit");
list2.add("world");
System.out.println(list2);
System.out.println("========");
//get 指定位置元素
System.out.println(list2.get(0));
System.out.println(list2.get(2));
System.out.println("========");
//set指定位置元素
list2.set(0,"a");
list2.set(2,"g");
System.out.println(list2);
}

public static void main(String[] args) {
//初始化
List<String> list = new ArrayList<>();
//添加元素
list.add("hello");
list.add("bit");
list.add("bit");
list.add("world");
System.out.println(list);
System.out.println("========");
//判断字段是否在线性表中
System.out.println(list.contains("bit"));
System.out.println("========");
//返回字段所在的下标
System.out.println(list.indexOf("bit"));//从头找
System.out.println(list.lastIndexOf("bit"));//从尾找
}

public static void main(String[] args) {
//初始化
List<String> list = new LinkedList<>();
//添加元素
list.add("hello");
list.add("bit");
list.add("bit");
list.add("world");
System.out.println(list);
System.out.println("========");
//截取部分list - 左闭右开区间
List<String> list1 = list.subList(0,1);
System.out.println(list1);
}

我们目前已知的LIST打印方法有两种:循环遍历、toString方法,但除此之外我们还有迭代器打印:
public static void main(String[] args) {
System.out.println("=====toString===");
ArrayList<String> list2 = new ArrayList<>();
list2.add("hello");
list2.add("bit");
list2.add("haha");
System.out.println(list2);
System.out.println("====for..each===");
for (String str:list2
) {
System.out.print(str + " ");
}
System.out.println();
System.out.println("======for..=====");
for (int i = 0; i < list2.size(); i++) {
System.out.print(list2.get(i) + " ");
}
System.out.println();
System.out.println("======迭代器=====");
Iterator<String> it = list2.iterator();
while (it.hasNext()){
System.out.print(it.next() + " ");
}
System.out.println();
System.out.println("==迭代器List用===");
ListIterator<String> listIterator = list2.listIterator();
while (listIterator.hasNext()){
System.out.print(listIterator.next() + " ");
}
}

我们通过查询官方文档可知,Iterator迭代器没有add方法,而ListIterator迭代器有add方法,两者的共同点是都有remove方法
public static void main(String[] args) {
ArrayList<String> list2 = new ArrayList<>();
list2.add("hello");
list2.add("bit");
list2.add("haha");
ListIterator<String> it = list2.listIterator();
while (it.hasNext()){
String ret = it.next();
if (ret.equals("hello")) {
it.add("ag");//使用了迭代器修改list2其中元素,指针指向ag
}else {
System.out.print(ret + " ");
}
}
System.out.println();
System.out.println(list2);
}

那么我们可不可以用ArrayList自己的add方法呢?
public static void main(String[] args) {
ArrayList<String> list2 = new ArrayList<>();
list2.add("hello");
list2.add("bit");
list2.add("haha");
ListIterator<String> it = list2.listIterator();
while (it.hasNext()){
String ret = it.next();//必须先迭代next所有元素,才能用remove删除
if (ret.equals("hello")) {
list2.add("ag");
}else {
System.out.print(ret + " ");
}
}
System.out.println();
System.out.println(list2);
}

此时我们发现代码报错了,这是因为我们的ArrayList是线程不安全的,要想直接用对象的add方法,我们就只能用CopyOnWriteArrayList,它是线程安全的
public static void main(String[] args) {
//ArrayList - 线程不安全 CopyOnWriteArrayList - 线程安全
//ArrayList list2 = new ArrayList<>();
CopyOnWriteArrayList list2 = new CopyOnWriteArrayList();
list2.add("hello");
list2.add("bit");
list2.add("haha");
System.out.println("==迭代器List用===");
ListIterator<String> listIterator = list2.listIterator();
while (listIterator.hasNext()){
//用了CopyOnWriteArrayList抛异常
String ret = listIterator.next();
if (ret.equals("hello")) {
//listIterator.add("ag");
list2.add("ag");
} else{
System.out.print(ret + " ");
}
}
System.out.println();
System.out.println(list2);
}

此时我们发现打印结果正确了;
二.🌕 ArrayList源码分析
不知道友友们想过一个问题没,当我们初始化不给定ArrayList空间时,它的默认空间是多少?0?
ArrayList<String> list2 = new ArrayList<>();//0?
Ctrl + 点击ArrayList

我们此时看到它定义了一个10的常量,还定义了一个空数组,它没事儿定义它干嘛?这其中必有蹊跷,我们往下拉查看它的构造方法

谜底揭晓,但数组空间为0时,它就会将顺序表初始化为一个空间为0的数组,那么那个空间大小为10的常量是干嘛的?
这就需要我们看看它的源码。看谁的?当然是add方法的,因为只有它添加元素时,才能看到其中的细节。
Ctrl + 点击add

此处我们看到它实现了一个方法,然后直接就开始添加了??!!我们想它都不判满的吗?而满后我们前面实现的顺序表是用的copyOf进行扩容,这方法其中必有猫腻
Ctrl + 点击ensureCapacityInternal

我们看到它传了一个方法的返回值到另一个方法中,我们先看看外内层方法是干嘛的
Ctrl + 点击calculateCapacity

谜底揭晓,当我们添加元素时,数组空间才会被初始化为10,不添加元素,数组空间还是原先的0
那么它是怎么扩容的呢?我们看看外层方法
Ctrl + 点击ensureExplicitCapacity

我们看到它其中的if语句,如果传入的参数大于了数组长度执行方法grow
Ctrl + 点击grow

终于我们看到了什么??!!Arrays.copyOf,至此我们便理解了ArrayList的add方法
家人们,学到这里我们的JAVA的List方法已经全部弄懂啦,🥳🥳🥳,后续新一会持续更JAVA的有关内容,学习永无止境,技术宅,拯救世界!