java面试基础 -- ArrayList 和 LinkedList有什么区别

目录

基本介绍

有什么不同??

ArrayList的扩容机制

ArrayLIst的基本使用


基本介绍

还记得我们的java集合框架吗, 我们来复习一下, 如图:

         可以看出来 ArrayList和LinkedList 都是具体类, 他们都是接口List的实现类.

但是他们底层的逻辑是不同的, 相信学过这个的应该大概有个映像吧, 如下图:

        可以看出来, ArrayList是向内存申请一块连续的数组空间进行存储, 在数组的存储形式的基础上进行链表的增删改查, 而LinkedList则是每次添加元素的时候就向系统申请一块内存, 不用就直接释放, 他们虽然在内存上不是连续的, 但是在逻辑上他们是连在一起的.

有什么不同??

  •  底层实现不同, ArrayList是基于动态数组的数据结构, 而LInkedLIst是基于链表的数据结构
  • 随机访问的性能不同, Arraylist的随机访问性能是由于LinkedList的, 因为ArrayList可以根据下标来直接访问, 类似于数组, 时间复杂度为O(1), 但是LinkedList的随机访问时间复杂度为O(n), 因为他需要遍历整个链表才能找到指定的元素
  • 插入和删除不同, 对于插入和删除, LInkedList要明显由于ArrayList, 因为LInkedList的掺入和删除操作时间复杂度为O(1), 例如插入, 我们可以直接在链表的头部进行插入或者是通过一个元素来记录最后一个结点的位置, 然后直接在最后一个结点进行尾插, 删除是相同的操作, 因此时间复杂度为O(1), 但是对于ArrayList就不同了, ArrayList的插入和删除需要移动插入位置的元素的后面的所有元素, 最坏的情况需要移动ArrayList的所有元素, 因此时间复杂度为O(n)

小结:

ArrayList 和 LinkedList 都是 List 接口的实现类,但它们的底层实现(结构)不同、随机访问的性能和添加/删除的效率不同。如果是随机访问比较多的业务场景可以选择使用 ArrayList,如果添加和删除比较多的业务场景可以选择使用 LinkedList。

ArrayList的扩容机制

我们首先创建一个ArrayList如图:

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;public class Test  {public static void main(String[] args) {List<Integer> arrayList = new ArrayList<>(); // 第0步:创建基于链表的ListarrayList.add(1);  // 1 添加元素arrayList.add(2);  // 2 添加元素arrayList.add(3);  // 3 添加元素arrayList.add(4);  // 4 添加元素arrayList.add(5);  // 5 添加元素arrayList.add(6);  // 6 添加元素arrayList.add(7);  // 7 添加元素arrayList.add(8);  // 8 添加元素arrayList.add(9);  // 9 添加元素System.out.println("hello");  // 打印}
}

第0步, 初始化:

ArrayList的构造方法如下:

    /*** Constructs an empty list with the specified initial capacity.** @param  initialCapacity  the initial capacity of the list* @throws IllegalArgumentException if the specified initial capacity*         is negative*/public ArrayList(int initialCapacity) {if (initialCapacity > 0) {this.elementData = new Object[initialCapacity];} else if (initialCapacity == 0) {this.elementData = EMPTY_ELEMENTDATA;} else {throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);}}/*** Constructs an empty list with an initial capacity of ten.*/public ArrayList() {this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;}/*** Constructs a list containing the elements of the specified* collection, in the order they are returned by the collection's* iterator.** @param c the collection whose elements are to be placed into this list* @throws NullPointerException if the specified collection is null*/public ArrayList(Collection<? extends E> c) {elementData = c.toArray();if ((size = elementData.length) != 0) {// c.toArray might (incorrectly) not return Object[] (see 6260652)if (elementData.getClass() != Object[].class)elementData = Arrays.copyOf(elementData, size, Object[].class);} else {// replace with empty array.this.elementData = EMPTY_ELEMENTDATA;}}

里面有三个重载的构造方法, 简单来说就是:

  1. 无参构造: Obeject数组elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
  2. 给定初始容量: 

    ①当 传入的初始容量initialCapacity > 0为真时,创建一个大小为initialCapacity的空数组,并将引用赋给elementData;

    ②当 传入的初始容量initialCapacity = 0为真时,将空数组EMPTY_ELEMENTDATA赋给elementData;

    ③当 传入的初始容量initialCapacity < 0为真时,直接抛出IllegalArgumentException异常。

此处我们传入的initialCapacity为空, 也就是无参构造方法, 如下:

transient Object[] elementData;private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};public ArrayList() {this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

         在构造方法中,它将DEFAULTCAPACITY_EMPTY_ELEMENTDATA赋值给elementData,这个DEFAULTCAPACITY_EMPTY_ELEMENTDATA是一个空的Object数组,而elementData就是ArrayList实际存储数据的容器

 第1步, 添加元素1:

触发:

   public boolean add(E e) {ensureCapacityInternal(size + 1);  // Increments modCount!!elementData[size++] = e;return true;}

ensureCapacityInternal为确认初始化容量:

进入ensureCapacityInternal:

    private void ensureCapacityInternal(int minCapacity) {ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));}

随后进入calculateCapacity计算最低所需容量:

    private static int calculateCapacity(Object[] elementData, int minCapacity) {if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {return Math.max(DEFAULT_CAPACITY, minCapacity);}return minCapacity;}

        此处的minCapacity为 size + 1, 翻译过来也就是最低所需的容量, 研究发现, 如果是空数组(刚开始使用无参构造方法的时候)就返回DEFAULT_CAPACITY, 值为10.

        当elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA 的时候, 直接返回minCapacity.

        随后进入ensureExplicitCapacity(int minCapacity)方法:

    private void ensureExplicitCapacity(int minCapacity) {modCount++;// overflow-conscious codeif (minCapacity - elementData.length > 0)grow(minCapacity);}

modCount++,这是用来记录数组被修改次数的变量,我们先不管它. minCapacity为计算出来的最小所需容量, elementData.length为当前容量,如果最小所需容量大于当前容量, 就需要扩容, 然后进入grow方法:

    private void grow(int minCapacity) {// overflow-conscious codeint 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);}

        老的容量为当前容量,新的容量为老的容量的1.5倍,如果新的容量–最小所需容量<О那么新的容量就等于最小所需容量,如果新的容量-当前数组最大的容量限制>0,那么就进入hugeCapacity方法,然后使用copyOf方法进行数据迁移.将老的数据迁移到新的容量的数组中

总结一下:

        我们使用无参构造方法的时候, 就会创建一个底层为空的数组的链表, 此时size 为0, 然后向里面添加元素的时候, 此时的minCapacity为 size + 1 = 1, 在calculateCapacity方法中返回了DEFAULT_CAPACITY(10), 然后进入ensureExplicitCapacity(10), 此时的minCapacity = 10 > 当前容量 = 0, 所以进行初始扩容(elementData = Arrays.copyOf(elementData, newCapacity)), 将容量扩充到10, 也就是elementData/length == 10, 随后的插入, 只要没有超过10, 那就可以直接插入, 如果容量满了, 那么就进行1.5倍扩容.

ArrayLIst的基本使用

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;public class Test  {public static void main(String[] args) {ArrayList<Integer> arrayList = new ArrayList<>(); // 第0步:创建基于链表的List// add(int x) 直接向元素末尾位置添加xarrayList.add(1);// get(int index) 方法, 返回下标为index的元素int a = arrayList.get(0);System.out.println(a);// add(int index, int element); 向指定位置插入元素arrayList.add(1,3);// size(); 获取当前元素的个数int size = arrayList.size();// remove(int index); 删除下标为index 的元素arrayList.remove(0);// 判断arrList是否为空, 如果为空就返回true, 否则返回false;arrayList.isEmpty();// set(int index, int element); 将index 下标的元素设置为 elementarrayList.set(0,5);}
}

LInkedList与之类似






本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/92627.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CI+JUnit5并发单测机制创新实践

目录 一. 现状问题 二. 分析原因 三. 采取措施 四. 实践步骤 五. 效能提升 资料获取方法 一. 现状问题 针对现如今高并发场景的业务系统&#xff0c;“并发问题” 终归是必不可少的一类&#xff08;占比接近10%&#xff09;&#xff0c;每次出现问题和事故后&#xff0c…

JVM基础了解

JVM 是java虚拟机。 作用&#xff1a;运行并管理java源码文件锁生成的Class文件&#xff1b;在不同的操作系统上安装不同的JVM&#xff0c;从而实现了跨平台的保证。一般在安装完JDK或者JRE之后&#xff0c;其中就已经内置了JVM&#xff0c;只需要将Class文件交给JVM即可 写好的…

Linux MQTT智能家居项目(LED界面的布局设置)

文章目录 前言一、LED界面布局准备工作二、LED界面布局三、逻辑实现总结 前言 上篇文章我们完成了主界面的布局设置那么这篇文章我们就来完成各个界面的布局设置吧。 一、LED界面布局准备工作 首先添加LED灯光控制的图标。 将选择好的LED图标添加进来&#xff1a; 图标可以…

SpringBoot 配置⽂件

SpringBoot 配置⽂件 1. 配置文件的作用2. .配置⽂件的格式2.1 properties2.1.1 基本语法2.1.2 读取配置⽂件 2.2 yml2.2.1 概念2.2.2 基本语法2.2.3 配置对象2.2.4 配置集合 2.3 properties 和 yml 对比 1. 配置文件的作用 整个项⽬中所有重要的数据都是在配置⽂件中配置的&a…

ubuntu安装jdk、emqx、nginx

一、安装jdk 要在Ubuntu上安装JDK 1.8&#xff0c;您可以按照以下步骤进行操作&#xff1a; 打开终端&#xff08;CtrlAltT&#xff09;。确保您的系统已更新&#xff1a; sudo apt update sudo apt upgrade安装OpenJDK 8&#xff1a; sudo apt install openjdk-8-jdk安装完成…

1.MySQL数据库的基本操作

数据库操作过程&#xff1a; 1.用户在客户端输入 SQL 2.客户端会把 SQL 通过网络发送给服务器 3.服务器执行这个 SQL,把结果返回给客户端 4.客户端收到结果,显示到界面上 数据库的操作 这里的数据库不是代表一个软件&#xff0c;而是代表一个数据集合。 显示当前的数据库 …

Java:正则表达式书写规则及相关案例:检验QQ号码,校验手机号码,邮箱格式,当前时间

正则表达式 目标:体验一下使用正则表达式来校验数据格式的合法性。需求:校验QQ号码是否正确&#xff0c;要求全部是数字&#xff0c;长度是(6-20&#xff09;之间&#xff0c;不能以0开头 首先用自己编写的程序判断QQ号码是否正确 public static void main(String[] args) {Sy…

@Param详解

文章目录 背景什么是ParamParam的使用方法使用方法&#xff1a;遇到的问题及因Param解决了什么问题使用与不使用对比 Param是如何进行映射的总结 背景 最近在开发过程中&#xff0c;在写mapper接口是在参数前加了Param注解&#xff0c;但是在运行的时候就会报错&#xff0c;说…

Golang 函数定义及使用

文章目录 一、函数定义格式二、函数定义及使用 一、函数定义格式 //func: 函数定义关键字 //function_name&#xff1a;函数名称 //parameter_List: 函数参数列表&#xff0c;多个时使用逗号拆分 //return_types&#xff1a;函数返回类型&#xff0c;返回多个值时使用逗号拆分…

ios swift alert 自定义弹框 点击半透明部分弹框消失

文章目录 1.BaseAlertVC2.BindFrameNumAlertVC 1.BaseAlertVC import UIKitclass BaseAlertVC: GLBaseViewController {let centerView UIView()override func viewDidLoad() {super.viewDidLoad()view.backgroundColor UIColor(displayP3Red: 0, green: 0, blue: 0, alpha:…

pytorch报错torch.cuda.is_available()结果false处理方法

文章目录 问题及起因问题起因 解决方法 问题及起因 问题 前几天跑项目&#xff0c;笔记本上的GPU可以正常跑起来。要跑VAE模型&#xff0c;重新安装了torch,GPU就无法使用了&#xff0c;我重新安装了 cuda,torch.cuda.is_available()的结果依然是False。 起因 配置项目环境…

如何使用Kali Linux进行密码破解?

今天我们探讨Kali Linux的应用&#xff0c;重点是如何使用它来进行密码破解。密码破解是渗透测试中常见的任务&#xff0c;Kali Linux为我们提供了强大的工具来帮助完成这项任务。 1. 密码破解简介 密码破解是一种渗透测试活动&#xff0c;旨在通过不同的方法和工具来破解密码…

磁力线试验+多图

今天要磨制一个钢针工具。磨下来很多的铁屑&#xff0c;灵机一动&#xff0c;何不来试验一下磁铁的磁力线。这可是难得的材料。 下放7颗强力磁铁&#xff0c;可见强力磁铁的磁力线非常集中。 下放直径4CM的喇叭磁铁 强力磁铁U型铁 强力磁铁E型铁氧体磁芯&#xff0c;可见磁力线…

侯捷 C++ part2 兼谈对象模型笔记——7 reference、const、new/delete

7 reference、const、new/delete 7.1 reference x 是整数&#xff0c;占4字节&#xff1b;p 是指针占4字节&#xff08;32位&#xff09;&#xff1b;r 代表x&#xff0c;那么r也是整数&#xff0c;占4字节 int x 0; int* p &x; // 地址和指针是互通的 int& r x;…

掌握Python的X篇_30_使用python解析网页HTML

本篇将会介绍beutifulsoup4模块&#xff0c;可以用于网络爬虫、解析HTML和XML&#xff0c;对于没有接触过前端&#xff0c;不了解HTML是如何工作的&#xff0c;需要先解释一下什么事HTML。 1. HTML 网页中的各种布局等的背后都是非常简单的纯文本格式&#xff0c;那种格式称为…

JAVASE---数组的定义与使用

数组的基本概念 什么是数组 数组是具有相同类型元素的集合&#xff0c;在内存中连续存储。 1. 数组中存放的元素其类型相同 2. 数组的空间是连在一起的 3. 每个空间有自己的编号&#xff0c;起始位置的编号为0&#xff0c;即数组的下标 数组的创建及初始化 数组的创建 T[…

Kafka-eagle监控平台

Kafka-Eagle简介 在开发工作中&#xff0c;当业务不复杂时&#xff0c;可以使用Kafka命令来进行一些集群的管理工作。但如果业务变得复杂&#xff0c;例如&#xff1a;需要增加group、topic分区&#xff0c;此时&#xff0c;再使用命令行就感觉很不方便&#xff0c;此时&#x…

C++ 泛型编程:函数模板

文章目录 前言一、什么是泛型编程二、函数模板三、函数模板的使用四、多参数函数模板五&#xff0c;示例代码&#xff1a;总结 前言 当需要编写通用的代码以处理不同类型的数据时&#xff0c;C 中的函数模板是一个很有用的工具。函数模板允许我们编写一个通用的函数定义&#…

【Apollo】阿波罗自动驾驶:塑造自动驾驶技术的未来

前言 Apollo (阿波罗)是一个开放的、完整的、安全的平台&#xff0c;将帮助汽车行业及自动驾驶领域的合作伙伴结合车辆和硬件系统&#xff0c;快速搭建一套属于自己的自动驾驶系统。 开放能力、共享资源、加速创新、持续共赢是 Apollo 开放平台的口号。百度把自己所拥有的强大、…

【CI/CD】基于 Jenkins+Docker+Git 的简单 CI 流程实践(上)

基于 JenkinsDockerGit 的简单 CI 流程实践&#xff08;上&#xff09; 在如今的互联网时代&#xff0c;随着软件开发复杂度的不断提高&#xff0c;软件开发和发布管理也越来越重要。目前已经形成一套标准的流程&#xff0c;最重要的组成部分就是 持续集成 及 持续交付、部署。…