java集合框架-List集合ArrayList和LinkedList详解
List 集合源码剖析
✅ ArrayList

底层是基于数组,(数组在内存中分配连续的内存空间)是对数组的升级,长度是动态的。
数组默认长度是10,当添加数据超越当前数组长度时,就会进行扩容,扩容长度是之前的1.5倍,要对之前的数组对象进行复制,所以只有每次扩容时相对性能开销大一些。
源码(jdk 1.8):
1. 添加元素(非指定位置)
// 1. 添加元素
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 每次添加元素都要对容量评估
elementData[size++] = e;
return true;
}
// 2. 评估容量
private void ensureCapacityInternal(int minCapacity) {
// 若果数组对象还是默认的数组对象
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
// 3. 进一步评估是否要进行扩容
private void ensureExplicitCapacity(int minCapacity) {
modCount++; // 记录ArrayList结构性变化的次数
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
步骤3中
if (minCapacity - elementData.length > 0) grow(minCapacity);
当 minCapacity 大于当前数组对象长度时 才进行扩容操作,也就是执行步骤 4的代码
// 4.复制产生新的数组对象并扩容
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 = Arrays.copyOf(elementData, newCapacity);
}
2. 指定位置添加元素
public void add(int index, E element) {
// 1
rangeCheckForAdd(index);
// 2
ensureCapacityInternal(size + 1);
// 3
System.arraycopy(elementData, index, elementData, index + 1,size - index);
// 4
elementData[index] = element;
// 5
size++;
}
rangeCheckForAdd(index);评估插入元素位置是否合理ensureCapacityInternal(size + 1);检查数组容量是否有当前元素数量 size +1 个大,因为后续进行数组复制要多出一个元素数组复制
System.arraycopy(elementData, index, elementData, index + 1,size - index);
System.arraycopy(src, srcPos, dest, destPos , length);
src:源数组;srcPos:源数组要复制的起始位置;index 是要插入元素的位置,所以要从当前开始复制
dest:目的数组; destPos:目的数组放置的起始位置;复制好的元素要放在插入位置的后面 所以 index+1
length:复制的长度。包括插入位置和后面的元素 = 当前元素数 - 插入位置
步骤执行元素赋值
步骤元素长度+1
如果ArrayLisy集合不指定位置添加元素,默认往数组对象的后面追加,所以数组对象的其他元素位置不变,没有什么性能开销,如果元素插入到数组对象的前面,而且是越往前,重新排序的数组元素就会越多性能开销越大,当然通过上述源码介绍中看到,通过数组复制的方式排序对性能影响也不大,
3.查找元素
// 获取指定位置元素的源码
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
直接返回当前查找位置的数组对象对应的下标位置的元素,高性能,速度很快。
4. ArraList 和 Vector
ArraList 和 Vector 都是基于数组实现,它俩底层实现原理几乎相同,只是Vector是线程安全的,ArrayLsit不是线程安全的,它俩的性能也相差无几。
✅ LinkedList
- LinkedList使用循环双向链表数据结构,它和基于数组的List相比是截然不同的,在内存中分配的空间不是连续的。
- LinkedList是由一系列表项组成的,包括数据内容、前驱表项、后驱表项,或者说前后两个指针
- 不管LinkedList集合是否为空,都有一个header表项。(前驱表项指向 最后一个 元素,后驱表项指向 第一个元素)
双向链表

表项

1. 添加元素
//1. 添加元素,不指定位置会添加到最后一个
public boolean add(E e) {
linkLast(e);
return true;
}
//2. 添加到最后一位(每次添加插入元素都会创建一个Node对象)
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++;
}
//3. 创建表项
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
2. 添加指定位置元素
public void add(int index, E element) {
checkPositionIndex(index); // 检查位置是否合理
if (index == size)
linkLast(element); // 如果插入位置等于集合元素长度就往后追加
else
linkBefore(element, node(index)); // 否则在当前位置元素前面创建新节点
}
void linkBefore(E e, Node<E> succ) {
// assert succ != null;
// 步骤一
final Node<E> pred = succ.prev;
// 步骤二
final Node<E> newNode = new Node<>(pred, e, succ);
// 步骤三
succ.prev = newNode;
// 步骤四
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}
步骤一:获取当前元素(未插入目前要插入的元素前)前指针指向的节点
步骤二:创建新节点,一个表项,前驱表项是前面的节点 步骤一得到,后驱表项指向的时当前位置的节点(未插入目前要插入的元素前)
步骤三:重新设置当前位置的节点(未插入目前要插入的元素前)的前驱指针指向的节点,也就是刚插入的创建的新节点
步骤四:是对创建新节点的前置节点的后驱表项进行修改设置
总结:对应LinkedList插入任意位置元素 我们只需创建一个新元素节点和移动前后两个表项的指针,其他表项无需任何操作,性能高;
3. LinkedList集合中的第一个和最后一个元素位置是确定的
最后一个元素和第一个元素的位置不需要遍历整个链表获取,每个LinkedList集合无论是否为空,都会有一个Header表项,Header表项的前驱指针始终指向最后一个元素,后驱指针指向第一个元素,所以可以说LinkedList集合中的第一个和最后一个元素位置是确定的。
4. 查找删除元素
循环双向链表结构中节点位置的确定需要根据前面的元素往后遍历查找或者后面的元素往前遍历查找
// 1. 获取指定位置元素
public E get(int index) {
checkElementIndex(index); // 首先判断位置是否有效
return node(index).item;
}
// 2. 根据位置对节点遍历查找
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) { // 如果下标位置在总长度中间之前,则从前往后遍历查找
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else { // 如果下标位置在总长度中间之后,则从后往前遍历查找
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
从上面的源码中可以看出如果LinkedList集合的长度很大,则每次查找元素都会遍历很多次,性能影响也会更大
删除任意位置的元素,是先要找到该元素,所以需要上一步提到的查找操作
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index)); // node(index) 是查找元素
}
ArrayList和LinkedLisy对比
Java集合框架概述 Java集合工具包位于Java.util包下.包括了非常多经常使用的数据结构,如数组.链表.栈.队列.集合.哈希表等.学习Java集合框架下大致能够分为例如以下五个部分:List ... 一.ArrayList 解决了数组的局限性,最常见的容器类,ArrayList容器的容量capacity会随着对象的增加,自动增长.不会出现数组边界的问题. package collection; ... ArrayList 这里关于ArrayList本来都读了一遍源码,并且写了一些了,突然在原来的笔记里面发现了收藏的有相关博客,大致看了一下,这些就是我要写的(╹▽╹),而且估计我还写不到博主的水平,这 ... 1.简介 现在这篇,我们来学习TestNG.xml文件,前面我们已经知道,TestNG就是运行这个文件来执行测试用例的.通过本篇,你可以进一步了解到:这个文件是配置测试用例,测试套件.简单来说,利用这 ... ArrayList是基于动态数组实现的list,而LinkedList是基于链表实现的list.所以,ArrayList拥有着数组的特性,LinkedList拥有着链表的特性. 优缺点 ArrayLi ... 线性表,链表,哈希表是常用的数据结构,在进行Java开发时,JDK已经为我们提供了一系列相应的类来实现基本的数据结构.这些类均在java.util包中.本文试图通过简单的描述,向读者阐述各个类的作用以 ... [Java集合]LinkedList详解前篇 一.背景 最近在看一本<Redis深度历险>的书籍,书中第二节讲了Redis的5种数据结构,其中看到redis的list结构时,作者提到red ... Unit04: 集合框架 . 集合操作 -- 线性表 操作集合元素相关方法 package day04; import java.util.ArrayList; import java.util.Co ... java基础解析系列(十)---ArrayList和LinkedList源码及使用分析 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder jav ... 1.引言 特别说明:本文内容仅用于即时通讯技术研究和学习之用,请勿用于非法用途.如本文内容有不妥之处,请联系JackJiang进行处理! 我司有关部门为了获取黑产群的动态,有同事潜伏在大量的黑产群 ... 群组页是程序内部维护的一个数据库,其中一张表groups,用于存放创建的群组,还有一张表thread_group,用于关联群组和系统短信数据库中的会话. 数据库应该这样设计 MySqliteHelpe ... 前言 多层网络的训练需要一种强大的学习算法,其中BP(errorBackPropagation)算法就是成功的代表,它是迄今最成功的神经网络学习算法. 今天就来探讨下BP算法的原理以及公式推导吧. 神 ... 特别说明,本文是在Windows64位系统下进行的,32位系统请下载相应版本的安装包,安装方法类似. 使用python开发,环境有Python2和 python3 两种,有时候需要两种环境切换使用,下 ... 原来运行正常的项目,突然在applicationContext.xml 文件头报错 总结一下网上的解决方案: 1.有可能网络状况不好导致 如果使用Maven构建项目,spring在加载xsd文件时总是 ... 1.程序结构分为三种:循环结构.分支结构.顺序结构. 本文主要讨论循环结构. 2.While循环 左下角:循环计数端子i,从0开始计数,每进行一次循环,i自动增加1. 右下角:循环条件端子,当循环达到 ... 作者:依乐祝 原文地址:https://www.cnblogs.com/yilezhu/p/9664977.html 上篇<Ocelot简易教程(二)之快速开始2>教大家如何快速跑起来一个 ... IndexedDB 浏览器数据库,是一个非关系型数据库,数据形式使用的是json,IndexedDB适合存储大量数据,它的API是异步调用的,当然他的api 也相对复杂. 当然浏览器数据存储 还有Lo ... 写这篇文章的目的是为了以后的项目中懒得再去配gulp,直接可以拿这篇博客中的来用,因为有时候配置还是挺烦人的. gulp相关插件的介绍 用法比较简单,假设大家都会用gulp,下面主要介绍一下一些插件的 ... 小程序默认使用的navigationBar只能设置颜色.文字,左侧返回按钮也是不可改变的,若要实现下方效果有解决方案,但是也有一定的问题. 1.更改app.json "window" ...java集合框架-List集合ArrayList和LinkedList详解的更多相关文章
随机推荐