1 继承结构图

ArrayList继承AbstractList,实现了List接口

2 构造函数

transient Object[] elementData;    // 数组保存元素

private int size;                  // 记录长度

size记录ArrayList的长度,elementData记录元素的值

transient是Java语言的关键字,用来表示一个域不是该对象串行化的一部分。当一个对象被串行化的时候,transient型变量的值不包括在串行化的表示中,然而非transient型的变量是被包括进去的。transient详细介绍如链接:transient关键字介绍

三种构造方法

public ArrayList(int initialCapacity)

参数为长度,必须为大于等于0整数,如果传入0,与第二种构造方法结果相同,都是建立一个空的Object数组

public ArrayList()

创建一个空的Object数组

public ArrayList(Collection <?extendsE> c)

如果参数不为空,则使用Collection的toArray方法,把c转为对象数组,否则创建一个空的Object数组

3 需要注意的方法

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

常使用的是set(E element)这个方法 set方法如果使用set(int index, E element)这个方法,index对应的位置如果已经有值,该值会被新set的对象替换,并且返回旧值

    public void add(int index, E element) {
rangeCheckForAdd(index); ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}

平时使用都是使用add(E element),如果使用了add(int index, E element)这个方法,从上图的实现来看,它会先把记录元素的数组长度加1,然后把index之后的元素全部都后移一位,然后把新插入的元素放在索引为index的位置

    public E remove(int index) {
rangeCheck(index); modCount++;
E oldValue = elementData(index); int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work return oldValue;
}

remove(int index)的删除方法是把index之后的元素都往前移动一个位置,所以如果你用for循环遍历ArrayList并且在循环中满足某个条件就remove掉一个元素,那么就会抛出IndexOutOfBoundsException

    public boolean remove(Object o) {
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;
}

Remove(Object o)会从前往后查找,找到第一个相等的对象进行删除,删除的原理也是后面的元素前移一位,所以遍历ArrayList最好用迭代器进行遍历。

    public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index); Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount int numMoved = size - index;
if (numMoved > 0)
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved); System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}

addAll(int index, Collection c)如果当前ArrayList的长度小于index,直接从index开始,把c的元素按顺序摆放,如果当前ArrayList的长度大于Index,则把c插入到当前ArrayList的index 到index+c.length的位置,再把原ArrayList的剩余元素接到后面

4 不常用方法

    private boolean batchRemove(Collection<?> c, boolean complement) {
final Object[] elementData = this.elementData;
int r = 0, w = 0;
boolean modified = false;
try {
for (; r < size; r++)
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
} finally {
// Preserve behavioral compatibility with AbstractCollection,
// even if c.contains() throws.
if (r != size) {
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
if (w != size) {
// clear to let GC do its work
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
}
}
return modified;
}

batchRemove不常用方法,可以批量删除当前list中包含(complement == true)或不包含(complement == false)某个集合c中所有元素。并返回操作的结果。

备注

上面说到了,直接使用ArrayList的remove在遍历中很容易出错,下面看看它的迭代器是怎么实现remove的

    private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount; Itr() {} public boolean hasNext() {
return cursor != size;
} public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification(); try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}

cursor记录要访问的下一个index,lastRet记录当前已经读到的index,迭代器在remove的时候,先调用ArrayList的remove方法删除对应元素,同时,会把cursor和lastRet都减1,自动帮我们完成了元素的重新定位。

本文来自我的个人博客:http://blog.duchangchun.com/2018/12/29/jdk_arraylist/

JDK源码阅读—ArrayList的实现的更多相关文章

  1. JDK源码阅读——ArrayList

    序 如同C语言中字符数组向String过渡一样,作为面向对象语言,自然而然的出现了由Object[]数据形成的集合.本文从JDK源码出发简单探讨一下ArrayList的几个重要方法. Fields / ...

  2. JDK源码阅读--ArrayList

    public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess ...

  3. JDK源码阅读(三):ArraryList源码解析

    今天来看一下ArrayList的源码 目录 介绍 继承结构 属性 构造方法 add方法 remove方法 修改方法 获取元素 size()方法 isEmpty方法 clear方法 循环数组 1.介绍 ...

  4. JDK源码阅读-FileDescriptor

    本文转载自JDK源码阅读-FileDescriptor 导语 操作系统使用文件描述符来指代一个打开的文件,对文件的读写操作,都需要文件描述符作为参数.Java虽然在设计上使用了抽象程度更高的流来作为文 ...

  5. JDK源码阅读(一):Object源码分析

    最近经过某大佬的建议准备阅读一下JDK的源码来提升一下自己 所以开始写JDK源码分析的文章 阅读JDK版本为1.8 目录 Object结构图 构造器 equals 方法 getClass 方法 has ...

  6. 利用IDEA搭建JDK源码阅读环境

    利用IDEA搭建JDK源码阅读环境 首先新建一个java基础项目 基础目录 source 源码 test 测试源码和入口 准备JDK源码 下图框起来的路径就是jdk的储存位置 打开jdk目录,找到sr ...

  7. JDK源码阅读-FileOutputStream

    本文转载自JDK源码阅读-FileOutputStream 导语 FileOutputStream用户打开文件并获取输出流. 打开文件 public FileOutputStream(File fil ...

  8. JDK源码阅读-FileInputStream

    本文转载自JDK源码阅读-FileInputStream 导语 FileIntputStream用于打开一个文件并获取输入流. 打开文件 我们来看看FileIntputStream打开文件时,做了什么 ...

  9. JDK源码阅读-ByteBuffer

    本文转载自JDK源码阅读-ByteBuffer 导语 Buffer是Java NIO中对于缓冲区的封装.在Java BIO中,所有的读写API,都是直接使用byte数组作为缓冲区的,简单直接.但是在J ...

随机推荐

  1. Java开发报表——Grid++Report 报表设计器

    为了让数据显示的更加形象生动,报表在项目中差点儿是很常见的,可是大致能够分为两类: 一,图形:以图形的形式显示数据,比如柱状图,折线图,饼形图等等,这里有许多关于这方面的工具,比如JFreeChart ...

  2. js进阶 11-14 jquery如何实现元素的替换和遍历

    js进阶  11-14  jquery如何实现元素的替换和遍历 一.总结 一句话总结:替换:replaceAll() 与 replaceWith().遍历:each(). 1.replaceAll() ...

  3. jquery file upload示例

    原文链接:http://blog.csdn.net/qq_37936542/article/details/79258158 jquery file upload是一款实用的上传文件插件,项目中刚好用 ...

  4. NetSnmp配置

    http://blog.csdn.net/shanzhizi/article/details/16985989

  5. Cocos2d-x 3.0final 终结者系列教程05-AppDelegate入口类

    下面是Cocos2d-x的程序入口: class  AppDelegate : private cocos2d::Application { public: AppDelegate(); virtua ...

  6. 【转载】FormsAuthenticationTicket 对象

    1.使用Forms验证存储用户自定义信息 Forms验证在内部的机制为把用户数据加密后保存在一个基于cookie的票据FormsAuthenticationTicket中,因为是经过特殊加密的,所以应 ...

  7. 利用performSelectorInBackground和performSelectorOnMainThread实现多线程

    NSObject类的performSelectorOnMainThread和performSelectorInBackground能够实现简单的多线程编程技术 1.- (void)performSel ...

  8. .netcore consul实现服务注册与发现-集群完整版

    原文:.netcore consul实现服务注册与发现-集群完整版 一.Consul的集群介绍    Consul Agent有两种运行模式:Server和Client.这里的Server和Clien ...

  9. [DevExpress]DevExpress 中 汉化包 汉化方法

    第一步: 在Debug 下加入 zh-CN 汉化包(自行下载) 第二步: 在 Program.cs中加入下面代码 : System.Threading.Thread.CurrentThread.Cur ...

  10. Opencv光流运动物体追踪

    光流的概念是由一个叫Gibson的哥们在1950年提出来的.它描述是空间运动物体在观察成像平面上的像素运动的瞬时速度,利用图像序列中像素在时间域上的变化以及相邻帧之间的相关性来找到上一帧跟当前帧之间存 ...