当前位置: 首页 > 编程日记 > 正文

Java中ArrayList源码分析

一、简介

ArrayList是一个数组队列,相当于动态数组。每个ArrayList实例都有自己的容量,该容量至少和所存储数据的个数一样大小,在每次添加数据时,它会使用ensureCapacity()保证容量能容纳所有数据。

1.1、ArrayList 的继承与实现接口

ArrayList继承于AbstractList,实现了List, RandomAccess, Cloneable, java.io.Serializable这些接口。

public class  ArrayList<E> extends AbstractList<E>
         implements List<E>, RandomAccess, Cloneable, java.io.Serializable
  • ArrayList 继承了AbstractList,实现了List。它是一个数组队列,提供了相关的添加、删除、修改、遍历等功能。
  • ArrayList 实现了RandmoAccess接口,即提供了随机访问功能。RandmoAccess是java中用来被List实现,为List提供快速访问功能的。在ArrayList中,我们即可以通过元素的序号快速获取元素对象;这就是快速随机访问。
  • ArrayList 实现了Cloneable接口,即覆盖了函数clone(),能被克隆。不过它实现的是对ArrayList 实例的浅拷贝。
  • ArrayList 实现java.io.Serializable接口,这意味着ArrayList支持序列化,能通过序列化去传输。

1.2、ArrayList 与Collection集合的关系

20131105100548[1]

1.3、ArrayList 与Vector的区别

ArrayList 与Vector大致等同,唯一的区别就是ArrayList的操作是线程不安全的,然而Vector是线程安全的。所以,建议在单线程中才使用ArrayList,而在多线程中可以选择Vector或者CopyOnWriteArrayList。

二、源码分析

对于ArrayList而言,它实现List接口、底层使用数组保存所有元素。其操作基本上是对数组的操作。

2.1、成员变量

private static final long serialVersionUID = 8683452581122892189L;    // 使用serialVersionUID验证版本一致性
private static final int DEFAULT_CAPACITY = 10;    // 容量的初始大小
private static final Object[] EMPTY_ELEMENTDATA = {};    // Shared empty array instance used for empty instances.
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};    // Shared empty array instance used for default sized empty instances.
// ArrayList数据的存储之地
transient Object[] elementData;    // 存储ArrayList中元素的数组
// ArrayList存储数据的个数
private int size;    

2.2、构造方法

ArrayList提供了三种方式的构造器方法:

1)通过传入的initialCapacity大小构造ArrayList

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);
    }
}

2)使用初始容量值构造ArrayList

public  ArrayList() 
{
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

3)通过传入的指定类集构造ArrayList

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;
    }
}

2.3、增加元素操作

1)往数组尾部添加元素

public boolean add(E e)
{
    // 1、确保容量大小
    ensureCapacityInternal(size + 1); // Increments modCount!!
    // 2、尾部添加元素
    elementData[size++] = e;
    return true;
}

2)在指定位置添加元素

public void add(int index, E element)
{
    // 1、检验索引
    rangeCheckForAdd(index);
    // 2、确保容量
    ensureCapacityInternal(size + 1); // Increments modCount!!
    // 3、将数据后移
    System.arraycopy(elementData, index, elementData, index + 1,
    size - index);
    // 4、添加元素
    elementData[index] = element;
    // 5、数组元素个数加1
    size++;
}
==>System.arraycopy(src, srcPos, dest, destPos, length);

3)往数组尾部添加某类集合中所有元素,针对泛型

public boolean addAll(Collection<? extends E> c)
{
    // 1、暂存集合c中数据
    Object[] a = c.toArray();
    int numNew = a.length;
    // 2、确保容量
    ensureCapacityInternal(size + numNew); // Increments modCount
    // 3、尾部添加数据
    System.arraycopy(a, 0, elementData, size, numNew);
    // 4、数组元素个数更新
    size += numNew;
    return numNew != 0;
}
注意:The behavior of this operation is undefined if the specified collection is modified while the operation is in progress.

4)在指定位置添加某类集合中所有元素,针对泛型

public boolean addAll(int index, Collection<? extends E> c)
{
    // 1、检验索引
    rangeCheckForAdd(index);
    // 2、暂存数据
    Object[] a = c.toArray();
    int numNew = a.length;
    // 3、确保容量
    ensureCapacityInternal(size + numNew); // Increments modCount
    int numMoved = size - index;
    // 4、数据后移
    if (numMoved > 0)
        System.arraycopy(elementData, index, elementData, index + numNew,
        numMoved);
    // 5、添加元素
    System.arraycopy(a, 0, elementData, index, numNew);
    // 6、数组元素个数更新
    size += numNew;
    return numNew != 0;
}

2.4、删除元素操作

1)删除ArrayList中第一个符合条件的元素

public boolean remove(Object o)
{
    // 移除值为null的元素
    if (o == null)
    {
        for (int index = 0; index < size; index++)
            if (elementData[index] == null)
            {
                fastRemove(index);
                return true;
            }
    }
    else  // 移除元素 
    {
        for (int index = 0; index < size; index++)
            if (o.equals(elementData[index]))
            {
                fastRemove(index);
                return true;
            }
    }
    // 值不存在时,移除失败
    return false;
}

==>

private void fastRemove(int index)
{
    // 1、修改的次数更新
    modCount++;
    int numMoved = size - index - 1;
    // 2、数据前移
    if (numMoved > 0)
        System.arraycopy(elementData, index + 1, elementData, index,
        numMoved);
    elementData[--size] = null; // clear to let GC do its work
}

2)删除ArrayList中指定位置上的元素

public E remove(int index)
{
    // 1、检验索引
    rangeCheck(index);
    modCount++;
    E oldValue = elementData(index);
    int numMoved = size - index - 1;
    // 2、数据前移
    if (numMoved > 0)
        System.arraycopy(elementData, index + 1, elementData, index,
        numMoved);
    elementData[--size] = null; // clear to let GC do its work
    return oldValue;
}

3)删除ArrayList中所包含传入集合类中的所有元素,针对泛型

public boolean removeAll(Collection<?> c)
{
    Objects.requireNonNull(c);
    return batchRemove(c, false);
}

4)删除某个范围的数据

protected void removeRange(int fromIndex, int toIndex)
{
    modCount++;
    int numMoved = size - toIndex;
    System.arraycopy(elementData, toIndex, elementData, fromIndex, numMoved);
    // clear to let GC do its work
    int newSize = size - (toIndex - fromIndex);
    for (int i = newSize; i < size; i++)
    {
        elementData[i] = null;
    }
    size = newSize;
}

5)删除ArrayList中不是所传入集合类中的所有元素,针对泛型

public boolean retainAll(Collection<?> c)
{
    Objects.requireNonNull(c);
    return batchRemove(c, true);
}

2.5、修改操作

1)修改指定位置上的元素

public E set(int index, E element)
{
    rangeCheck(index);
    E oldValue = elementData(index);
    elementData[index] = element;
    return oldValue;
}

2.6、查找操作

1)获取指定位置上的元素

public E get(int index)
{
    rangeCheck(index);
    return elementData(index);
}

2)获取指定元素在列表中的第一个位置索引

public int indexOf(Object o)
{
    if (o == null)
    {
        for (int i = 0; i < size; i++)
            if (elementData[i] == null)
                return i;
    } else
    {
        for (int i = 0; i < size; i++)
            if (o.equals(elementData[i]))
                return i;
    }
    return -1;
}

3)获取指定元素在列表中的最后一个位置索引

public int lastIndexOf(Object o)
{
    if (o == null)
    {
        for (int i = size - 1; i >= 0; i--)
            if (elementData[i] == null)
                return i;
    } else
    {
        for (int i = size - 1; i >= 0; i--)
            if (o.equals(elementData[i]))
                return i;
    }
    return -1;
}

4)返回某个范围的视图

public List<E> subList(int fromIndex, int toIndex)
{
    subListRangeCheck(fromIndex, toIndex, size);
    return new SubList(this, 0, fromIndex, toIndex);
}
扩展:java List.subList方法中的超级大陷阱

5)迭代器

a>
public Iterator<E> iterator()
{
    return new Itr();
}

b>

public ListIterator<E> listIterator()
{
    return new ListItr(0);
}

扩展:JAVA中ListIterator和Iterator详解与辨析

c>

public ListIterator<E> listIterator(int index)
{
    if (index < 0 || index > size)
        throw new IndexOutOfBoundsException("Index: " + index);
    return new ListItr(index);
}

2.7、状态操作

1)是否为空

public boolean isEmpty()
{
    return size == 0;
}

2.8、其它常用操作

1)清空列表所有元素

public void clear()
{
    modCount++;
    // clear to let GC do its work
    for (int i = 0; i < size; i++)
        elementData[i] = null;
    size = 0;
}

2)克隆ArrayList实例的副本,浅拷贝

public Object clone()
{
    try
    {
        ArrayList<?> v = (ArrayList<?>) super.clone();
        v.elementData = Arrays.copyOf(elementData, size);
        v.modCount = 0;
        return v;
    } catch (CloneNotSupportedException e)
    {
        // this shouldn't happen, since we are Cloneable
        throw new InternalError(e);
    }
}

3)判断是否包含某个指定元素

public boolean contains(Object o)
{
    return indexOf(o) >= 0;
}

4)获取元素的个数

public int size()
{
    return size;
}

2.9、辅助操作

1)确保容量大小

public void ensureCapacity(int minCapacity)
{
    int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
        // any size if not default element table
        ? 0
        // larger than default for default empty table. It's already
        // supposed to be at default size.
        : DEFAULT_CAPACITY;
    if (minCapacity > minExpand)
    {
        ensureExplicitCapacity(minCapacity);
    }
}

2)裁剪容量

public void trimToSize()
{
    modCount++;
    if (size < elementData.length)
    {
        elementData = (size == 0)
        ? EMPTY_ELEMENTDATA
        : Arrays.copyOf(elementData, size);
    }
}

3)转换成数组

a> 该操作没有涉及到引用,属于安全操作

public Object[] toArray()
{
    return Arrays.copyOf(elementData, size);
}
b> 针对泛型
public <T> T[] toArray(T[] a)
{
    if (a.length < size)
        // Make a new array of a's runtime type, but my contents:
        return (T[]) Arrays.copyOf(elementData, size, a.getClass());
    System.arraycopy(elementData, 0, a, 0, size);
    if (a.length > size)
        a[size] = null;
    return a;
}

三、扩展区

1、专题一、ArrayList增删操作技术细节详解

2、Java提高篇(三四)-----fail-fast机制 (转载)

3、专题二、ArrayList序列化技术细节详解

4、专题三、ArrayList遍历方式以及效率比较

5、Java集合源码剖析(转载)

参考:

http://www.jb51.net/article/42764.htm

http://zhangshixi.iteye.com/blog/674856

转载于:https://www.cnblogs.com/aoguren/p/4756941.html

相关文章:

介绍三种绘制时间线图的方法

作者 |周萝卜来源 |萝卜大杂烩今天我们再来分享几种不同的制作方法&#xff0c;大家可以自行比较下各种方法的优劣。Matplotlib 制作Matplotlib 作为 Python 家族最为重要的可视化工具&#xff0c;其基本的 API 以及绘制流程还是需要掌握的。尤其是该库的灵活程度以及作为众多工…

phpize是什么

安装php&#xff08;fastcgi模式&#xff09;的时候&#xff0c;常常有这样一句命令&#xff1a;/usr/local/webserver/php/bin/phpize 一、phpize是干嘛的&#xff1f; phpize是什么东西呢&#xff1f;php官方的说明&#xff1a; http://php.net/manual/en/install.pecl.phpiz…

C语言比较好的风格梳理

errno int err;tb malloc(sizeof(struct xtracer_table));if (!tb) {err errno;fprintf(stderr, "%s:%d, errno:%d, %s\n",__func__, __LINE__, err, strerror(err));return NULL;} 转载于:https://www.cnblogs.com/muahao/p/8979144.html

Linux下Memcache服务器端的安装

Linux下Memcache服务器端的安装服务器端主要是安装memcache服务器端&#xff0c;目前的最新版本是 memcached-1.3.0 。下载&#xff1a;http://www.danga.com/memcached/dist/memcached-1.2.2.tar.gz http://memcached.googlecode.com/files/memcached-1.4.9.tar.gz 另外&#…

如何用技术恢复模糊的图像?在线教学…

作者 |小白 来源 |小白学视觉 有人认为恢复模糊的图像是不可能的&#xff0c;因为会丢失信息。但我对这个问题进行了很多思考&#xff0c;并认为如果输出图像的大小与输入图像的大小相同&#xff0c;那实际上是可能的&#xff01;这样&#xff0c;输出就有足够的像素/信息来恢复…

数据库创建索引的原则

数据库建立索引的原则 铁律一&#xff1a;天下没有免费的午餐&#xff0c;使用索引是需要付出代价的 索引的优点有目共睹&#xff0c;但是&#xff0c;却很少有人关心过采用索引所需要付出的成本。若数据库管理员能够对索引所需要付出的代价有一个充分的认识&#xff0c;也就不…

Linux上实现ssh免密码登陆远程服务器

平常使用ssh登陆远程服务器时&#xff0c;都需要使用输入密码&#xff0c;希望可以实现通过密钥登陆而免除输入密码&#xff0c;从而可以为以后实现批量自动部署主机做好准备。 环境如下&#xff1a; IP地址操作系统服务器端10.0.0.10CentOS 6.5 x86客户端10.0.0.61CentOS 6.5 …

分享memcache和memcached安装过程

Memcache是什么&#xff1f; Memcache是一个自由和开放源代码、高性能、分配的内存对象缓存系统。用于加速动态web应用程序&#xff0c;减轻数据库负载。 它可以应对任意多个连接&#xff0c;使用非阻塞的网络IO。由于它的工作机制是在内存中开辟一块空间&#xff0c;然后建立一…

导入导出Android手机文件

1、获得root权限&#xff1a;adb root&#xff1b; 如提示adbd cannot run as root in production builds&#xff0c;参见我的另一篇文章&#xff1a;http://www.cnblogs.com/hdk1993/p/4770388.html 2、设置/system为可读写&#xff1a;adb remount&#xff1b; 3、将文件复制…

GPT-3 不够 Open,BigScience 构建开放语言模型,规模小 16 倍

编译 | 禾木木 出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09; 大约一年前&#xff0c;总部位于纽约布鲁克林的自然语言处理初创公司 Hugging Face 推出了 BigScience。这是一个拥有 900 多名研究人员的国际项目&#xff0c;旨在更好地理解自然语言模型原理和提高大…

华为云微服务引擎CSE大量新特性上线,诚邀您免费体验

1、提供GO语言微服务开发框架SDK 支持插件化注册中心、多RPC协议&#xff08;已默认实现http和highway&#xff0c;可扩展&#xff09; 提供熔断降级、容错、路由管理、限流、错误注入、灰度发布等治理能力 2、提供Service Mesh商业版 支持.NET、Node.js、PHP等多语言应用…

memcache和memcached安装

首先要明确 memcache不是memcached第一步安装libevent #wget https://github.com/downloads/libevent/libevent/libevent-2.0.15-stable.tar.gz #tar libevent-2.0.15-stable.tar.gz #tar xzvf libevent-2.0.15-stable.tar.gz #cd libevent-2.0.15-stable #./configure --h…

点击按钮下载文件

RequestMapping("/download.do")public void download(HttpServletRequest request,HttpServletResponse response)throws Exception {String filePath "文件路径";FileInputStream fis null;OutputStream os null;try {fis new FileInputStream(fileP…

开源社区的危机:拒绝被“白嫖”?2大著名项目遭作者破坏

作者 | 林檎来源 | 数据实战派近日&#xff0c;一位开源开发者的故意破坏&#xff0c;再次引发了机构依赖开源库的争议。这一类开源库往往由维护者义务工作而支撑。被破坏的开源库是 Marak Squires 开发的 color.js 库和 faker.js 库。这两个库被广泛使用&#xff0c;其中不乏企…

状态和面向对象编程——1.定位步骤

定位 所有无人驾驶车要安全畅游全球&#xff0c;都必须经过一系列相同的步骤。 你一直在学习第一步&#xff1a;定位。在车辆能够安全驾驶之前&#xff0c;它们首先要使用传感器和收集的其他数据对它们所处的位置做出最佳估计。 卡尔曼滤波器 让我们来回顾一下卡尔曼滤波器对汽…

ldconfig命令详解,linux动态链接库

动态链接库管理命令 为了让动态链接库为系统所共享,还需运行动态链接库的管理命令--ldconfig.此执行程序存放在/sbin目录下. ldconfig命令的用途,主要是在默认搜寻目录(/lib和/usr/lib)以及动态库配置文件/etc/ld.so.conf内所列的目录下,搜索出可共享的动态链接库(格式如前介绍…

用于自动驾驶的实时 YUV 多任务 CNN

作者 | AI 修炼之路来源 | AI 修炼之路摘要本文提出了一种针对低功耗车用SoC优化的多任务卷积神经网络(CNN)结构。我们介绍了一个基于统一架构的网络&#xff0c;其中编码器由检测和分割两个任务共享。该网络以25FPS运行&#xff0c;分辨率为1280800。简要讨论了直接利用原生YU…

博客5:文件,目录以及用户的权限管理

linux用户与组的相关内容简介&#xff1a; 1.Linux用户&#xff1a;Username/UID管理员&#xff1a;root&#xff0c;0普通用户&#xff1a;1-65535系统用户&#xff1a;1-499&#xff08;在centos7上为1-999&#xff09;作用&#xff1a;对守护进程获取资源进行权限分配登录…

以太坊代币空投合约的实现

2019独角兽企业重金招聘Python工程师标准>>> 本文将介绍如何在以太坊智能合约中实现代币的空投。区块链以太坊世界中所谓空投&#xff08;airdrop&#xff09;&#xff0c;就是免费给你的区块链地址&#xff08;公钥&#xff09;发送代币。 代币空投的方式层出不穷&…

linux命令:ln 使用方法

命令&#xff1a;ln 使用方法指令名称 : ln使用权限 : 所有使用者使用方式 : ln [options] source dist&#xff0c;其中 option 的格式为 :[-bdfinsvF] [-S backup-suffix] [-V {numbered, existing, simple}][--help] [--version] [--] 说明 : Linux/Unix 档案系统中&#xf…

10 个案例分享几个 Python 可视化小技巧,助你绘制高质量图表

作者 | 俊欣来源 | 关于数据分析与可视化一般在Python当中&#xff0c;我们用于绘制图表的模块最基础的可能就是matplotlib了&#xff0c;今天小编分享几个用该模块进行可视化制作的技巧&#xff0c;帮助你绘制出更加高质量的图表。同时本篇文章的第二部分是用Python来制作可视…

(转) 地区赛获胜策略,赛前默念!

1. 比赛中评测会有些慢&#xff0c;偶尔还会碰到隔10分钟以上才返回结果的情况&#xff0c;这段时间不能等结果&#xff0c;必须开工其他题&#xff0c;如果WA&#xff0c;两道题同时做。交完每道题都要先打印。2. 比赛时发的饭不是让你当时就吃的&#xff0c;那是给你赛后吃的…

USG防火墙telnet实验

实验使用USG5500防火墙 &#xff0c;<SRG>system-view [SRG]interface g0/0/0       [SRG-GigabitEthernet0/0/0]ip address 192.168.1.1 24          接口配置地址[SRG-GigabitEthernet0/0/0]display this&#xff08;显示当前配置&#xff09; [SRG-G…

如何营造专属你的企业技术影响力氛围感?我不允许你还不知道

CSDN 推出《开发者研究与洞察》服务。基于3200万开发者的资源&#xff0c;从开发者视角出发&#xff0c;聚焦开发者“关注”、“使用”、“体验”三方面&#xff0c;帮助技术推广者打造技术品牌、优化技术产品的市场投放策略、提升技术产品的开发者使用体验&#xff0c;直接聆听…

php报错Permission denied

去apache的log下看error_log文件 #cd /usr/local/apache2/logs/ (13)Permission denied: exec of ....index.php failed加权限就可以 #chmod x index.php路径

Spring笔记——8.基于XML Schema的简化配置

我们可以使用XML Schema的配置方式来简化xml文件的配置。p&#xff1a;简化设值注入p&#xff1a;与property子元素作用相同&#xff0c;用于设值注入。若想使用p&#xff0c;则xml文件中需要引入对p的说明&#xff0c;一般自动生成的xml都会自带。xmlns:p"http://www.spr…

测试服务命名和动态注册路由的方式@Xan

2019独角兽企业重金招聘Python工程师标准>>> 1、测试服务命名&#xff1a;如不需要网关进行权限和登录验证时&#xff0c;服务名称命名后面加“tests”&#xff0c;例如&#xff1a; sysadmintests 2、动态注册路由地址&#xff1a; http://192.168.2.164:55551/sys…

POJ1386 Play on Words

题意&#xff1a;判断一些单词能不能首尾连成一体 #include <iostream> #include <algorithm> #include <cstring> #include <cmath> #include <cstdio> using namespace std; int n,father[30],range[30],save[100010],in[30],out[30]; bool us…

Linux tail 命令详解

用途从指定点开始将文件写到标准输出。使用tail命令的-f选项可以方便的查阅正在改变的日志文件&#xff0c;tail -f filename会把filename里最尾部的内容显示在屏幕上&#xff0c;并且不但刷新&#xff0c;使你看到最新的文件内容。 语法标准语法tail [ -f ] [ -c Number | …

万粉博主推荐,微信小程序 +Flask 后端调用 AnimeGanV2

作者 | Yunlord博客 | Yunlord做一个小程序&#xff0c;直接在手机端就能一键生成专属于自己的动漫头像&#xff0c;下面是展示效果&#xff01;&#xff01;&#xff01;核心功能设计该小程序想要实现的是将微信头像或者选择相册中的照片动漫化&#xff0c;所以拆解需求后&…