Java集合之ArrayList - 吃透增删查改

从源码看初始化以及增删查改,学习ArrayList。

先来看下ArrayList定义的几个属性:

private static final int DEFAULT_CAPACITY = 10;
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object[] elementData; // 保存对象
private int size; // ArrayList的长度

从这里可以看到ArrayList内部使用数组实现的。

一. 初始化

1. ArrayList()

无参的构造器代:

	/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

可以看到这个构造器初始化了一个空数组。这里有个疑问,就是注释明明说是构造了一个长度为10的数组,其实这是在添加第一个元素的时候才进行的扩充。

2. ArrayList(int initialCapacity)

指定长度的构造器:

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

这个构造器显式的指明了数组的长度,其实如果小于10的话,在添加第一个元素的时候还是会扩充到长度为10的数组。

二. 增加元素

1. add(E e)

	/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#add})
* 添加指定的元素到List的末尾
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 扩充数组长度
elementData[size++] = e; // 最后一个赋值为e
return true;
}

那么它是如何扩充数组长度的呢?追踪代码来到grow(int minCapacity)方法:

/*
* 可以看到它是通过Arrays.copyOf方法将原数组拷贝到了一个数组长度为newCapacity的新数组里面
*/
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. add(int index, E element) **

	/**
* 在指定的位置添加一个元素
*/
public void add(int index, E element) {
rangeCheckForAdd(index); // 检查index是否合法 ensureCapacityInternal(size + 1); // 扩充数组长度
System.arraycopy(elementData, index, elementData, index + 1,
size - index); // 通过拷贝返回一个新数组
elementData[index] = element;
size++;
}

这里用到了System类的arraycopy方法,它的用法如下:

System. arraycopy(Object src, int srcPos, Object dest, int destPos,int length)

src:原数组;

srcPos:源数组要复制的起始位置;

dest:目标数组;

destPos:目标数组放置的起始位置;

length:复制的长度

这个方法也可以实现自身的复制。上面的就是利用了自身赋值的特性进行数组的拷贝。相当于将index后面的所有元素后移了一位,将新元素插入到index位置。

三. 删除元素

1. remove(int index)

 	/*
* 和add(int index, E element)类似,使用System.arraycopy进行自身拷贝,相当于将index后面的元素
* 像前移动了一位,覆盖掉需要删除的元素,将最后一个元素置位null,等待JVM自动回收
*/
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;
}

2. remove(Object o)

	/*
* 先通过for循环找到o所在的位置,再通过fastRemove(index)移除,实现方法和remove(int index)一样
*/
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;
}

四. 查找元素

	/*
* 查找元素就比较简单了,直接通过数组的下角标进行返回
*/
public E get(int index) {
rangeCheck(index); return elementData(index);
}

五. 修改元素

	/*
* 修改元素也比较简单,直接通过数组的下角标进行赋值
*/
public E set(int index, E element) {
rangeCheck(index); E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}

总结

从源码可以看到,ArrayList以数组方式实现,查找和修改的性能比较好,增加和删除的性能就比较差了。

从源码看Java集合之ArrayList的更多相关文章

  1. 从源码看java中Integer的缓存问题

    在开始详细的说明问题之前,我们先看一段代码 public static void compare1(){ Integer i1 = 127, i2 = 127, i3 = 128, i4 = 128; ...

  2. 从源码看java线程状态

    关于java线程状态,网上查资料很混乱,有的说5种状态,有的说6种状态,初学者搞不清楚这个线程状态到底是怎么样的,今天我讲一下如何看源码去解决这个疑惑. 直接上代码: public class Thr ...

  3. JDK源码看Java域名解析

    前言 在互联网中通信需要借助 IP 地址来定位到主机,而 IP 地址由很多数字组成,对于人类来说记住某些组合数字很困难,于是,为了方便大家记住某地址而引入主机名和域名. 早期的网络中的机器数量很少,能 ...

  4. 源码之Java集合

    No1: ArrayList的扩容策略是,新容量扩大为原来的1.5倍. ArrayList不是线性安全的,因为没有使用synchronized关键字,但是优点是效率提高了.与之相比,Vector是线性 ...

  5. 源码:Java集合源码之:哈希表(二)

    要想知道一个元素是否在数组或链表中,只能从前向后挨个对比,无论是数组还是链表,其对数据的查询表现都比较无力.在的二叉排序树中,还会将数据排序以进行二分查找,将时间复杂度从O(n)降低到O(lg n). ...

  6. 源码:Java集合源码之:数组与链表(一)

    数组和链表是数据结构中最基本的部分. 数组 在java中,数组定义为一种基本类型,其可以通过下标获取到对应位置的数据.那么这种结构的数据,在内存中是怎么存放的呢? 数组在内存中是一段连续的存储单元,每 ...

  7. Java集合(六)--ArrayList、LinkedList和Vector对比

    在前两篇博客,学习了ArrayList和LinkedList的源码,地址在这: Java集合(五)--LinkedList源码解读 Java集合(四)--基于JDK1.8的ArrayList源码解读 ...

  8. Java集合源码分析(二)ArrayList

    ArrayList简介 ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长,类似于C语言中的动态申请内存,动态增长内存. ArrayList不是线程安全的,只能用在单线程环境下,多线 ...

  9. 死磕 java集合之ArrayList源码分析

    欢迎关注我的公众号"彤哥读源码",查看更多源码系列文章, 与彤哥一起畅游源码的海洋. 简介 ArrayList是一种以数组实现的List,与数组相比,它具有动态扩展的能力,因此也可 ...

随机推荐

  1. Could not retrieve transaction read-only status from server问题排查

    今天发现save task的时候经常后台会报这个错,而且有的时候还会卡住等20几分钟才执行完. 2019-11-12 15:08:29.410 http-nio-9080-exec-6 ERROR o ...

  2. xposed获取context 的方法

    // 应用被加壳,采用这种方式加载类 try { XposedHelpers.findAndHookMethod(Application.class, "attach", Cont ...

  3. 《python解释器源码剖析》第12章--python虚拟机中的函数机制

    12.0 序 函数是任何一门编程语言都具备的基本元素,它可以将多个动作组合起来,一个函数代表了一系列的动作.当然在调用函数时,会干什么来着.对,要在运行时栈中创建栈帧,用于函数的执行. 在python ...

  4. H265编码等级以及图像的基础知识

    1. H265编码等级 H264编码profile & level控制 .H265编码初探 H265 profile H265 Profile & Level & Tier 介 ...

  5. 用Python来使用科大讯飞语音识别,so easy

    在人工智能高速发展的今天,语音识别技术被带入到人们的工作和生活中,开始被越来越多的人关注和使用,今天,当各种在线客服被机器人客服代替,当速记翻译馆被语音识别代替,甚至当收银员.驾驶员.工厂工人.普通文 ...

  6. java与.net_20190726

    特性与注解 方法首字母 Java没有ref和out关键字 Java的main方法与C#的Main方法不一样: Java的io类和C#不一样. java的jsp和aspx还是很类似的. Java的ser ...

  7. 通过字节码分析Java方法的静态分派与动态分派机制

    在上一次[https://www.cnblogs.com/webor2006/p/9723289.html]中已经对Java方法的静态分派在字节码中的表现了,也就是方法重载其实是一种静态分派的体现,这 ...

  8. python+mysql:实现一千万条数据插入数据库

    作业要求 构建一个关系模式和课本中的关系movies(title,year,length,movietype,studioname,producerC)一样的关系,名称自定,在这个关系中插入1000万 ...

  9. asp.net中gridview控件的一些基本使用方法

    [ 转自苏飞博客]共两篇 (1)菜单目录: GridView无代码分页排序GridView选中,编辑,取消,删除GridView正反双向排序GridView和下拉菜单DropDownList结合Gri ...

  10. Python+request 将获取的url和接口响应时间(timeout)写入到Excel中《八》

    使用场景: 在工作中,常见某个功能的查询,当查询关联表特别多时,开发写的SQL语句等等问题导致响应时间较慢,那么作为测试需要将每个接口的响应时间进行测试,对同个接口多次执行,并将测试结果写入到exce ...