枕上诗书闲处好,门前风景雨来佳。这篇文章主要讲述ArrayListVectorLinkedList的底层源码分析和对比相关的知识,希望能为你提供帮助。
(一)List接口的概述在前面我们讲完了Collection的特点和使用,接下来就开始讲Collection的子接口和实现类,List具有以下两个特点:
1.有序(不是指按数值大小有序排列,而是指插入和取出的顺序是固定的),因为List通过下标记录值
2.可重复,List可以添加重复的值,也可以添加重复的空值
List继承了Collection,所以Collection中的方法它都能使用,当然List也有属于自己的特有方法,下面就来介绍一下。
(二)List的特有方法
public class ListTest1 {
public static void main(String[] args) {
List list = new ArrayList();
//特有方法1.add(index,element)
list.add(0,"a");
list.add(1,"a");
list.add(2,null);
list.add(3,"b");
System.out.println(list);
//特有方法2.list.get(index)
System.out.println(list.get(0));
//特有方法3.list.indexOf(element) 查找第一个element的索引
System.out.println(list.indexOf("a"));
//特有方法4.list.remove(index)
list.remove(0);
System.out.println(list);
//特有方法5.list.set(index,element)
list.set(0,"d");
System.out.println(list);
}
}
?
因为List是继承Collection的,因此迭代器遍历和增强for遍历都适用于List,同时因为List存在下标,因此也可以像遍历数组一样遍历LIst。
(三)ArrayList的底层和源码分析【ArrayListVectorLinkedList的底层源码分析和对比】集合的使用并不难,因此我们有必要去理解集合底层的原理和看它的部分代码。
?
以上这一段是ArrayList的官方文档介绍,来自java Platform SE 8。这段文字的意思翻译过来就是指ArrayList是个可变数组,拥有了List的所有操作,运行任何元素,包括null。它还有属于自己的一些方法。ArrayList和Vector很相似,但是ArrayList是unsynchronized,即它不是线程同步的。
3.1 底层结构我们已经知道了ArrayList的底层就是一个数组,在JDK8中:当初始化时,ArrayList中会初始化一个Object数组,容量为0;当要添加数据时,初始容量变为10,当第11个元素要进来时,容量扩容为15,当第16个元素要进来时,容量扩容为22。当每次要扩容时,都会扩容成原来容量的1.5倍。下面通过调试来分析一下部分源码:
3.1.1 首先写一个简单的arrayList添加元素的代码
public class ListTest2 {
public static void main(String[] args) {
ArrayList list=new ArrayList();
for (int i=1;
i<
35;
i++){
list.add(i);
}
}
}
将断点设置在初始化ArrayList的位置,进入ArrayList构造方法,初始化时,ArrayList内部数组的容量被设置为0
public ArrayList(){
this.elementData = https://www.songbingjia.com/android/DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
接着进入add方法:
public boolean add(E e) {
ensureCapacityInternal(size + 1);
// Increments modCount!!
elementData[size++] = e;
return true;
}
add方法并不复杂,首先进入ensureCapacityInternal()方法判断容量是否足够,容量足够就添加数据,进入ensureCapacityInternal方法:
private void ensureCapacityInternal(int minCapacity) {
if (elementData =https://www.songbingjia.com/android/= DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
当第一次添加数据时,minCapacity=0,满足if语句的条件,则通过Math.max方法设置第一次扩容的minCapacity=10,进入ensureExplicitCapacity()方法
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length >
0)
grow(minCapacity);
}
此时minCapacity=10,elementData.length=0,首先对操作数加1,接着判断minCapacity于elementData长度的差值,大于0(即minCapacity容量不够用时)则进入grow函数
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >
>
1);
if (newCapacity - minCapacity <
0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE >
0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = https://www.songbingjia.com/android/Arrays.copyOf(elementData, newCapacity);
}
?
grow函数用于对数组进行扩容,(oldCapacity > > 1)相当于(oldCapacity / 2)。第一次进入时minCapacity=10,oldCapacity =0,所以第四行的运算结果还是等于0,新的容量就变为10。第二次进入时(即minCapacity=11),newCapacity 就等于15,也就是我们之前所讲的1.5倍。
(四)Vector与ArrayList的对比vector目前已经很少用了,因此我们不过多介绍,主要说一下它和ArrayList的区别
Vector和ArrayList很像,底层也是一个数组,和ArrayList不同的是,Vector是线程安全的,它使用了synchronized关键词:
????
文章图片
在扩容倍数上,ArrayList是1.5倍,而Vector的扩容分为两种:一种是不指定扩容增量的情况下就是2倍扩容;第二种是制定了的扩容增量length的情况下,就是每次都在原来容量的基础上扩容length长度。下面是Vector的grow函数源码:
文章图片
?
(五)LinkedList的底层和源码分析LinkedList的底层结构是一个双向链表,拥有List的所有操作。
首先是两个重要的元素first和last,分别指向首结点和尾结点
另外每个节点(Node)也有三个属性:item、next、prev分别表示当前元素,前一个和后一个。
我们还是通过单步调试去看看LinkedList的内部执行逻辑,首先写一条简单的代码:
public class LinkedList1 {
public static void main(String[] args) {
LinkedList linkedList=new LinkedList();
for (int i=1;
i<
10;
i++){
linkedList.add(i);
}
}
}
初始化的构造方法不包含其他的代码,直接进入add方法:
public boolean add(E e) {
linkLast(e);
return true;
}
然后再进入linkLast方法:
void linkLast(E e) {
final Node<
E>
l = last;
final Node<
E>
newNode = new Node<
>
(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
这段代码也容易理解,先设置l指向当前节点,再创建新节点,前指向l,后指向null。再根据是否是第一个插入的数据完成双链表的连接。
(六)三个实现类的对比通过表格来表示三者之前的差距:
文章图片
如何选择:
如果考虑线程安全问题:Vector
不考虑线程安全:
增删多:LinkedList
改查多:ArrayList
文章图片
?
?
?
?
?
推荐阅读
- 鸿蒙开源全场景应用开发——视频编解码
- 测试平台系列(52)改造异步数据库连接方案
- 9.6-9.12上周精彩回顾
- vivo营销自动化技术解密|开篇
- 网络--路由器
- 安全系列之:跨域资源共享CORS
- ui自动化__unittest单元测试框架
- 密码学系列之:海绵函数sponge function
- 自定义帖子类型常规帖子类型