java集合专题List接口ArrayList/Vector/LinkedList底层结构和源码分析
大家好,我是bug郭,一名双非科班的在校大学生。对C/JAVA、数据结构、Linux及MySql、算法等领域感兴趣,喜欢将所学知识写成博客记录下来。 希望该文章对你有所帮助!如果有错误请大佬们指正!共同学习交流
作者简介:
- CSDN java领域新星创作者blog.csdn.net/bug…
- 掘金LV3用户 juejin.cn/user/bug…
- 阿里云社区专家博主,星级博主,developer.aliyun.com/bug…
- 华为云云享专家 bbs.huaweicloud.com/bug…
我们学习List下面ArrayList,LinkedList,Vector
类即可!
学习的重点就是了解他们的底层结构,扩容原理,通过源码进行学习!
List接口下的一些常用方法
@SuppressWarnings("all")
public class ArrayList_ {
public static void main(String[] args) {
List list = new ArrayList();
//添加单个元素
for (int i = 0; i <10; i++) {
list.add(i);
}
//将list集合转换成数组进行打印!
System.out.println(Arrays.toString(list.toArray()));
//添加list集合
List list1 = new ArrayList();
list1.add(666);
list1.add(888);
//在1索引位置插入list1集合
list.addAll(1,list1);
//迭代器打印!
Iterator iterator = list.iterator();
while(iterator.hasNext()){
System.out.print(iterator.next()+" ");
}
//删除元素
list.remove(new Integer(666));
//判断是否包含元素!
list.contains(666);
//删除整个list
list.removeAll(list1);
//返回最后一个9的下标位置!
System.out.println("\n"+Arrays.toString(list.toArray()));
System.out.println("从后往前找9的下标:"+list.lastIndexOf(9));
System.out.println("6的下标:"+list.indexOf(6));
System.out.println("拆分list集合");
List tmp = list.subList(5,10);
System.out.println("tmp:"+Arrays.toString(tmp.toArray()));
//更改元素!
System.out.println("更改前:"+list.get(0)+list.set(0,9)+"更改后:"+list.get(0));
//排序:传入排序接口!
System.out.println("排序前:"+Arrays.toString(list.toArray())+"\n排序后:");
list.sort(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
return (Integer) o1 - (Integer)o2;//升序
}
});
//增强for遍历!
for (Object object : list) {
System.out.print(object+" ");
}
}
}
ArrayList
底层结构
ArrayList底层是由数组实现的!
-
ArrayList维护了一个
elementData
数组,由transient
修饰不可序列化! -
ArrayList扩容机制:
- 无参构造器,默认大小为0,第一次添加元素,扩容为10,后面扩容按照1.5倍进行
- 有参构造器,默认容量指定参数值大小,不够进行1.5倍扩容
源码分析
我们通过IDEA调试从而分析ArrayList底层结构和扩容机制
无参构造器扩容机制源码分析
我们通过下面的代码进行ArrayList
无参扩容机制的分析
public class ArrayList_ {
public static void main(String[] args) {
//无参扩容机制!
ArrayList list = new ArrayList();
//默认为 0 第一次添加扩容为 10
for (int i = 0; i <10; i++) {
list.add(i);
}
//后续扩容1.5!
list.add(11);
}
}
- 无参构造器初始化
elementData
数组长度为0
- 第一次添加
- 装箱,因为这里的数据是int基本类,要进行装箱成Integer类!
第一次扩容
进行扩容后elementData数组长度就变成了10
当添加第11个元素时,进行二次扩容
调用int newCapacity = oldCapacity + (oldCapacity>>1);
进行1.5倍扩容!
从而数组长度变成了15
有参构造器扩容机制源码分析
public class ArrayList_1 {
public static void main(String[] args) {
//有参扩容机制,初始化指定容量大小
ArrayList list = new ArrayList(5);
for (int i = 0; i < 5; i++) {
list.add(i);
}
//进行第一次1.5倍扩容
list.add(6);
}
}
总结:
每次add
都会检查数组容量,是否要进行扩容!
- 无参扩容机制
elementData
数组默认为0,当进行第一次add
进行第一次扩容,扩容大小为10,而后面进行二次扩容就是1.5倍扩容!
- 有参扩容机制
elementData
数组默认为指定参数长度,第一次扩容时,1.5倍扩容!
Vector
底层结构
Vector类和ArrayList类底层的结构一样也是数组!
不过和ArrayList区别就是,扩容策略不同,Vector是线程安全的(每个方法都加了synchronized),适用于多线程,而ArrayList线程不安全!
扩容策略
- 无参构造器扩容策略,初始化容量为10,后续2倍扩容
- 有参构造器,初始化指定参数容量,后续2倍扩容
- 可设置增量的构造器,指定初始化容量,后续扩容按增量进行
源码分析
我们学习Vector构造方法后,发现Vector有个构造器,可以设置容量的增量!
可以自定义扩容的增量值!
扩容机制
通过下面的代码debug进行扩容机制的学习!
public class Vector_ {
public static void main(String[] args) {
//Vector无参扩容机制!
Vector vector = new Vector();
//一次扩容
for (int i = 0; i <10 ; i++) {
vector.add(i);
}
//进行二次扩容
vector.add(10);
}
}
无参构造器,初始化容量为10
第一次扩容
有参扩容机制和无参扩容机制类似,初始化时指定数组长度!扩容策略一样!
LinkedList
底层结构
LinkedList
类和上述2个不同,他的底层采用的是双向链表的方式存储数据
- LinkedList底层实现了双向链表和双端队列的特点
- 可以添加任意元素包括null
- 线程不安全,没有实现线程同步
- 采用链表的方式存储,增删效率高,不需要扩容!
可以看到LinkedList
实现了List
接口和DeQue
接口!
Node节点
这里通过frist
保存头Node
节点,last
保存尾节点!
因为LinkedList实现了Deque双端队列接口,所以其下的方法都实现了
方法使用
package list;
import java.util.Arrays;
import java.util.LinkedList;
public class LinkedList_ {
public static void main(String[] args) {
LinkedList linkedList = new LinkedList();
linkedList.add(1);
linkedList.addLast(2);
linkedList.addFirst(0);
System.out.println("获取队首元素:"+linkedList.getFirst());
System.out.println("获取队尾元素:"+linkedList.getLast());
System.out.println(Arrays.toString(linkedList.toArray()));
System.out.println("出队:"+linkedList.poll());
System.out.println(Arrays.toString(linkedList.toArray()));
System.out.println("对尾删除元素:"+linkedList.pollLast());
linkedList.push(12);
linkedList.pop();
System.out.println(Arrays.toString(linkedList.toArray()));
}
}
注意:
这里的方法有点多:
offer/poll
push/pop
add/remove
这里只有push/pop
不能进行First/Last
组合,其他都可!
源码分析
public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<>();
list.add(1);
list.remove();
list.get(1);
}
add添加节点
默认队尾入队
remove删除节点
默认删除第一个节点!
get方法获取节点
三者比较
底层结构 | 增删效率 | 改查效率 | |
---|---|---|---|
ArrayList/Vector | 可变数组 | 较低,要扩容 | 较高 |
LinkedList | 双向链表 | 较高,通过链表修改指针即可 | 较低 |
三者如何抉择:
- 如果改查操作多且单线程使用ArrayList,如果多线程就vector!
- 如果增删操作多,那就LinkedList
- 一般来说查询基本上查询为主,所以首选ArrayList!
- 点赞
- 收藏
- 关注作者
评论(0)