聊聊 Java 集合框架中的 ArrayList

news2024/11/21 0:17:30

其实 Java 集合框架也叫做容器,主要由两大接口派生而来,一个是 collection,主要存放对象的集合。另外一个是Map, 存储着键值对(两个对象)的映射表。

下面就来说说 List接口,List存储的元素是有序、可重复的。其下有三个子接口,ArrayList、LinkedList 和 vector。

一、 ArrayList概述

ArrayList 底层数据结构是基于 Object 数组来实现的,我们看看它的底层接口源码:

1. ArrayList 实现的接口

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable

其中继承的接口中的 RandomAccessCloneableSerializable 只是标记接口,他们的接口内部没有具体的方法和参数:

public interface RandomAccess {} 
public interface Cloneable {}    //覆盖clone(),能被克隆
public interface Serializable {} //支持序列化,能通过序列化传输

标记接口是计算机科学中的一种设计思路。编程语言本身不支持为类维护元数据。而标记接口则弥补了这个功能上的缺失——一个类实现某个没有任何方法的标记接口,实际上标记接口从某种意义上说就成为了这个类的元数据之一。运行时,通过编程语言的反射机制,我们就可以在代码里拿到这种元数据。

以Serializable接口为例。一个类实现了这个接口,说明它可以被序列化。因此,我们实际上通过Serializable这个接口,给该类标记了“可被序列化”的元数据,打上了“可被序列化”的标签。这也是标记/标签接口名字的由来。

此外AbstractList 继承AbstractCollection 抽象类,实现List接口。它实现了 List 的一些基本操作如(get,set,add,remove),是第一实现随机访问方法的集合类,但是不支持添加和替换。

2.ArrayList 的成员属性

private static final int DEFAULT_CAPACITY = 10; //默认初始容量为10 
private static final Object[] EMPTY_ELEMENTDATA = {}; //空数组,用于空实例
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};//用于默认大小空实例的共享空数组实例。
transient Object[] elementData; //保存ArrayList数据的数组,transient修饰表示数组默认不会被序列化
private int size; //ArrayList中数组的个数

二、ArrayList 中的方法

Java ArrayList 中常用的方法:

方法描述
add()将元素插入到指定位置的 arraylist 中
addAll()添加集合中的所有元素到 arraylist 中
clear()删除 arraylist 中的所有元素
clone()复制一份 arraylist
contains()判断元素是否在 arraylist
get()通过索引值获取 arraylist 中的元素
indexOf()返回 arraylist 中元素的索引值
removeAll()删除存在于指定集合中的 arraylist 里的所有元素
remove()删除 arraylist 里的单个元素
size()返回 arraylist 里元素数量
isEmpty()判断 arraylist 是否为空
subList()截取部分 arraylist 的元素
set()替换 arraylist 中指定索引的元素
sort()对 arraylist 元素进行排序
toArray()将 arraylist 转换为数组
toString()将 arraylist 转换为字符串
ensureCapacity()设置指定容量大小的 arraylist
lastIndexOf()返回指定元素在 arraylist 中最后一次出现的位置
retainAll()保留 arraylist 中在指定集合中也存在的那些元素
containsAll()查看 arraylist 是否包含指定集合中的所有元素
trimToSize()将 arraylist 中的容量调整为数组中的元素个数
removeRange()删除 arraylist 中指定索引之间存在的元素
replaceAll()将给定的操作内容替换掉数组中每一个元素
removeIf()删除所有满足特定条件的 arraylist 元素
forEach()遍历 arraylist 中每一个元素并执行特定操作

具体的方法细节可以看这里

三、ArrayList 中的扩容机制

在初始化时,ArrayList 有三种方式来进行初始化,以无参构造方法创建 ArrayList 时,实际上赋值的是一个空数组。当真正对数组进行添加元素时,才真正的给 ArrayList 分配容量,即数组容量扩为10。


/**
     *默认构造函数,使用初始容量10构造一个空列表(无参数构造)
     */
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
     * 带初始容量参数的构造函数。(用户自己指定容量)
     */
public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {//初始容量大于0
        //创建initialCapacity大小的数组
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {//初始容量等于0
        //创建空数组
        this.elementData = EMPTY_ELEMENTDATA;
    } else {//初始容量小于0,抛出异常
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}


/**
    *构造包含指定collection元素的列表,这些元素利用该集合的迭代器按顺序返回
    *如果指定的集合为null,throws NullPointerException。
    */
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;
    }
}
/**
     * 将指定的元素追加到此数组的末尾。
     */
public boolean add(E e) {
    //在增加元素前,先调用ensureCapacityInternal方法
    ensureCapacityInternal(size + 1);  
    elementData[size++] = e;
    return true;
}

private void ensureCapacityInternal(int minCapacity) {
    //比较当前元素个数和默认元素个数,如果小于10,则将最小容量设为默认值10
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    //继续调用 ensureExplicitCapacity()方法
    ensureExplicitCapacity(minCapacity);
}

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    // overflow-conscious code
    //如果大于当前数组默认长度,则进行扩容,调用grow()方法
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}
/**
     * 要分配的最大数组大小
     */
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    //在以前的容量基础上增加旧容量的1/2
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    //检查比较新容量与最小容量的大小,取大值
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    //更新完新容量后,比较是否大于最大数组大小 Integer.MAX_VALUE - 8
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        //若大于最大数组大小,则调用hugeCapacity()方法
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
}

private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    //对minCapacity和MAX_ARRAY_SIZE进行比较
    //若minCapacity大,将Integer.MAX_VALUE作为新数组的大小
    //若MAX_ARRAY_SIZE大,将MAX_ARRAY_SIZE作为新数组的大小
    //MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
    MAX_ARRAY_SIZE;
}

四、ArrayList 相关面试题

1. System.arraycopy() 和 Arrays.copyOf() 的区别

联系:

看两者源代码可以发现 copyOf()内部实际调用了 System.arraycopy() 方法

区别:

arraycopy() 需要目标数组,将原数组拷贝到你自己定义的数组里或者原数组,而且可以选择拷贝的起点和长度以及放入新数组中的位置 copyOf() 是系统自动在内部新建一个数组,并返回该数组。

2. ArrayList 和 LinkedList 的区别

  1. 是否保证线程安全: ArrayListLinkedList 都是不同步的,也就是不保证线程安全;
  2. 底层数据结构: Arraylist 底层使用的是 Object 数组LinkedList 底层使用的是 双向链表 数据结构(JDK1.6 之前为循环链表,JDK1.7 取消了循环。注意双向链表和双向循环链表的区别,下面有介绍到!)
  3. 插入和删除是否受元素位置的影响:ArrayList 采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响。 比如:执行add(E e)方法的时候, ArrayList 会默认在将指定的元素追加到此列表的末尾,这种情况时间复杂度就是 O(1)。但是如果要在指定位置 i 插入和删除元素的话(add(int index, E element))时间复杂度就为 O(n-i)。因为在进行上述操作的时候集合中第 i 和第 i 个元素之后的(n-i)个元素都要执行向后位/向前移一位的操作。 ② LinkedList 采用链表存储,所以对于add(E e)方法的插入,删除元素时间复杂度不受元素位置的影响,近似 O(1),如果是要在指定位置i插入和删除元素的话((add(int index, E element)) 时间复杂度近似为o(n))因为需要先移动到指定位置再插入。
  4. 是否支持快速随机访问: LinkedList 不支持高效的随机元素访问,而 ArrayList 支持。快速随机访问就是通过元素的序号快速获取元素对象(对应于get(int index)方法)。
  5. 内存空间占用: ArrayList 的空 间浪费主要体现在在 list 列表的结尾会预留一定的容量空间,而 LinkedList 的空间花费则体现在它的每一个元素都需要消耗比 ArrayList 更多的空间(因为要存放直接后继和直接前驱以及数据)。

3. ArrayList 和 Vector 的区别

  1. ArrayListList 的主要实现类,底层使用 Object[ ]存储,适用于频繁的查找工作,线程不安全 ;
  2. VectorList 的古老实现类,底层使用 Object[ ]存储,线程安全的。如下代码带有 synchronized 同步锁,能够保证线程安全。
public synchronized void ensureCapacity(int minCapacity) {
    if (minCapacity > 0) {
        modCount++;
        ensureCapacityHelper(minCapacity);
    }
}

4. ArrayList 和 CopyOnWriteArrayList 的区别

CopyOnWriteArrayList 和 Vector 一样也是线程安全的 List 。它在 java.util.concurrent 的包下。它的线程安全主要是通过读写分离来实现的。写操作在一个复制的数组上实现(加锁),读操作还是在原数组中进行。

public boolean add(E e) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        Object[] elements = getArray();
        int len = elements.length;
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        newElements[len] = e;
        setArray(newElements);
        return true;
    } finally {
        lock.unlock();
    }
}

final void setArray(Object[] a) {
    array = a;
}

特点:适合读多写少的应用场景

缺点

  • 内存占用:在写操作时需要复制一个新的数组,使得内存占用为原来的两倍左右;
  • 数据不一致:读操作不能读取实时性的数据,因为部分写操作的数据还未同步到读数组中;

五、参考资料

ArrayList 扩容机制分析

Java ArrayList

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Harbor配置同步规则删除不掉

【问题原因】 harbor上主从两个仓库&#xff0c;配置同步规则时&#xff0c;定时任务配置太频繁&#xff0c;导致规则修改&#xff0c;删除都失败。 【问题现象】 点击修改后保存&#xff0c;页面报internal server error的错。 【问题排查】 docker ps | grep harbor 查看…

解决VirtualBox rc=-1908的错误

日常虽然使用linux办公&#xff0c;但是还是用virtualbox安装了一个20G的Windows作为ps等不可替代软件的作业环境。 每次Linux滚动更新&#xff08;尤其是内核更新&#xff09;后&#xff0c;virtualbox经常会遇到趴窝的情况。经过多方查证,有时可以解决问题,有时又不行,并且网…

数据库开发工具Navicat Premium 15 mac软件特色

Navicat Premium 15 mac版是一款数据库开发工具&#xff0c;Navicat Premium 15 Mac版可以让你以单一程序同時连接到 MySQL、MariaDB、SQL Server、SQLite、Oracle 和 PostgreSQL 数据库。 Navicat Premium mac软件特色 无缝数据迁移 数据传输&#xff0c;数据同步和结构同步…

[软件工具]AI软件离线表格识别工具使用教程图像转excel转表格可复制文字表格导出实时截图识别成表格

【官方框架地址】 https://github.com/PaddlePaddle/PaddleOCR.git 【算法介绍】 PaddleOCR是一个基于PaddlePaddle框架的开源光学字符识别&#xff08;OCR&#xff09;工具库&#xff0c;由百度公司开发。它提供了一套完整的OCR解决方案&#xff0c;包括文字检测、文字识别以…

MySQL连续案例续集

01&#xff09;查询学过「张三」老师授课的同学的信息 SELECT s.*, c.cname, t.tname, sc.score FROM t_mysql_teacher t, t_mysql_course c, t_mysql_student s, t_mysql_score sc WHERE t.tid c.tid AND c.cid sc.cid AND sc.sid s.sid AND t.tname ‘张三’ 02&#x…

Unity中BRP下的深度图

文章目录 前言一、在Shader中使用1、在使用深度图前申明2、在片元着色器中 二、在C#脚本中开启摄像机深度图三、最终效果 前言 在之前的文章中&#xff0c;我们实现了URP下的深度图使用。 Unity中URP下使用屏幕坐标采样深度图 在这篇文章中&#xff0c;我们来看一下BRP下深度…

「实用分享」如何用Telerik UI组件创建可扩展的企业级WPF应用?

Telerik UI for WPF拥有超过100个控件来创建美观、高性能的桌面应用程序&#xff0c;同时还能快速构建企业级办公WPF应用程序。UI for WPF支持MVVM、触摸等&#xff0c;创建的应用程序可靠且结构良好&#xff0c;非常容易维护&#xff0c;其直观的API将无缝地集成Visual Studio…

OpenAI ChatGPT-4开发笔记2024-04:Chat之Tool之2:multiple functions

从程序员到ai Expert 1 定义参数和函数2 第一轮chatgpt3 第一轮结果和function定义全部加入prompt再喂给chatgpt4 大结局7 参考资料 上一篇解决了调用一个函数的问题。这一篇扩展为调用3个。n个自行脑补。 1 定义参数和函数 #1.设定目标 import json import openai#1.定义para…

ADS仿真 之 瞬态仿真

瞬态仿真常用于低频模拟和数字电路的仿真&#xff0c;是用来模拟电压或者电流随时间的变化趋势&#xff0c; ADS在Simulation-transient面板中提供了与瞬态仿真相关的控件&#xff0c; 主要是瞬态仿真控件&#xff0c;一般的瞬态仿真主要关注时间的设置和时间的控制方式&#x…

理论U4 集成学习

文章目录 一、集成学习1、传统学习的问题2、集成学习1&#xff09;背景2&#xff09;概念3&#xff09;注意 3、多样性度量4、多样性增强1&#xff09;多样性增强&#xff1a;在学习过程引入随机性2&#xff09; 输入属性扰动3&#xff09; 输出表示扰动4&#xff09; 算法参数…

利用 Vesta 软件绘制团簇构型 ICO多面体(二)

微信公众号&#xff1a;原子与分子模拟更新的快&#xff0c;欢迎大家关注。 -------------------------------------------------------------------- 进入网站下载 ICO 文件&#xff1a; http://www.pas.rochester.edu/~wangyt/algorithms/bop/ 利用 Vesta 软件进行绘制。 …

SpringIOC之support模块EmbeddedValueResolutionSupport

博主介绍&#xff1a;✌全网粉丝5W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战&#xff0c;博主也曾写过优秀论文&#xff0c;查重率极低&#xff0c;在这方面有丰富的经验…

基于SELinux三权分立配置方法

1.系统安装 系统安装完成后,系统当前的SELinux配置为: # cat /etc/selinux/config SELINUX=enforcing SELINUXTYPE=targeted 2.SELinux环境准备 # yum install setools policycoreutils.x86_64 selinux-policy-mls.noarch setroubleshoot.x86_64 setools-console -y 3.SELin…

排序链表[中等]

一、题目 给你链表的头结点head&#xff0c;请将其按 升序 排列并返回 排序后的链表 。 示例 1&#xff1a; 输入&#xff1a;head [4,2,1,3] 输出&#xff1a;[1,2,3,4] 示例 2&#xff1a; 输入&#xff1a;head [-1,5,3,4,0] 输出&#xff1a;[-1,0,3,4,5] 示例 3&…

Spring MVC MVC介绍和入门案例

1.SpringMVC概述 1.1.MVC介绍 MVC是一种设计模式&#xff0c;将软件按照模型、视图、控制器来划分&#xff1a; M&#xff1a;Model&#xff0c;模型层&#xff0c;指工程中的JavaBean&#xff0c;作用是处理数据 JavaBean分为两类&#xff1a; 一类称为数据承载Bean&#xf…

python_数据可视化_pandas_导入excel数据

目录 1.1导入库 1.2读取excel文件 1.3读取excel&#xff0c;指定sheet2工作表 1.4指定行索引 1.5指定列索引 1.6指定导入列 案例速览&#xff1a; 1.1导入库 import pandas as pd 1.2读取excel文件 pd.read_excel(文件路径) data pd.read_excel(D:/desktop/TestExcel…

基于图像合成和注意力的深度神经网络从计算机断层扫描灌注图像中自动分割缺血性脑卒中病变

Automatic ischemic stroke lesion segmentation from computed tomography perfusion images by image synthesis and attention-based deep neural networks 基于图像合成和注意力的深度神经网络从计算机断层扫描灌注图像中自动分割缺血性脑卒中病变背景贡献实验Comparison o…

SCT2A23STER:4.5V-100V Vin, 1.2A, DCDC降压转换器

• 4.5V-100V 输入电压范围 • 1.2A 连续输出电流 • 1.8A峰值电流限制 • 室温下1.2V 1% 反馈电压 • 集成 530mΩ 上管和 220mΩ 下管功率 MOSFETs • 带VCC二极管的静态电流为15uA 无VCC二极管的静态电流为160uA • 可选PFM、USM和FPWM轻载工作模式 • 4.3ms 内置软启动时…

第一次面试总结 - 迈瑞医疗 - 软件测试

&#x1f9f8;欢迎来到dream_ready的博客&#xff0c;&#x1f4dc;相信您对专栏 “本人真实面经” 很感兴趣o (ˉ▽ˉ&#xff1b;) 专栏 —— 本人真实面经&#xff0c;更多真实面试经验&#xff0c;中大厂面试总结等您挖掘 注&#xff1a;此次面经全靠小嘴八八&#xff0c;没…

论文阅读:Making Large Language Models A Better Foundation For Dense Retrieval

论文链接 Abstract 密集检索需要学习区分性文本嵌入来表示查询和文档之间的语义关系。考虑到大型语言模型在语义理解方面的强大能力&#xff0c;它可能受益于大型语言模型的使用。然而&#xff0c;LLM是由文本生成任务预先训练的&#xff0c;其工作模式与将文本表示为嵌入完全…