愿每个人都能遵循自己的时钟,做不后悔的选择
大家好,这里是新一,请多关照🙈🙉🙊。在本篇博客中,新一将会为大家介绍JAVA数据结构 - List源码分析及模拟实现,干货满满哟。(以下结果均在IDEA中编译)希望在方便自己复习的同时也能帮助到大家。😜😜😜🥇🥈🥉
废话不多说,直接进入我们的文章。
文章目录
一.🌕 List的使用
👇👇👇
List官方文档戳这里
Array官方文档戳这里
LinkedList官方文档戳这里
1.1🍂 List常见方法
1.1.1 🍃 initialize && add
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);
}
1.1.2 🍃 remove && clear
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);
}
1.1.3 🍃 get && set
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);
}
1.1.4 🍃 contains && indexOf
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"));//从尾找
}
1.1.5 🍃 subList
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);
}
1.2🍂 List迭代器
我们目前已知的LIST
打印方法有两种:循环遍历
、toString方法
,但除此之外我们还有迭代器打印:
1.2.1 🍃 迭代器打印
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() + " ");
}
}
1.2.2 🍃 迭代器比较
我们通过查询官方文档可知,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<String> 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源码分析
2.1🍂 未初始化List空间大小分析
不知道友友们想过一个问题没,当我们初始化不给定ArrayList空间时,它的默认空间是多少?0?
ArrayList<String> list2 = new ArrayList<>();//0?
Ctrl + 点击ArrayList
我们此时看到它定义了一个10的常量,还定义了一个空数组,它没事儿定义它干嘛?这其中必有蹊跷,我们往下拉查看它的构造方法
谜底揭晓,但数组空间为0时,它就会将顺序表初始化为一个空间为0的数组
,那么那个空间大小为10的常量是干嘛的?
2.2🍂 add方法源码分析
这就需要我们看看它的源码。看谁的?当然是add
方法的,因为只有它添加元素时,才能看到其中的细节。
Ctrl + 点击add
此处我们看到它实现了一个方法,然后直接就开始添加了??!!我们想它都不判满的吗?而满后我们前面实现的顺序表是用的copyOf进行扩容
,这方法其中必有猫腻
Ctrl + 点击ensureCapacityInternal
我们看到它传了一个方法的返回值到另一个方法中,我们先看看外内层方法是干嘛的
Ctrl + 点击calculateCapacity
谜底揭晓,当我们添加元素时,数组空间才会被初始化为10,不添加元素,数组空间还是原先的0
那么它是怎么扩容的呢?我们看看外层方法
Ctrl + 点击ensureExplicitCapacity
我们看到它其中的if语句,如果传入的参数大于了数组长度执行方法grow
Ctrl + 点击grow
终于我们看到了什么??!!Arrays.copyOf
,至此我们便理解了ArrayList的add方法
想对大家说的话
家人们,学到这里我们的JAVA的List方法已经全部弄懂啦,🥳🥳🥳,后续新一会持续更JAVA的有关内容,学习永无止境,技术宅,拯救世界!