java源码研究--ArrayList实现

java.util.ArrayList是十分常用的容器之一,本文针对其常用方法,对其进行简单的研究。
ArrayList常见方法如下,主要还是增删改查:

首先,看一下ArrayList中如何保存数据的:

transient Object[] elementData;

所以,所有的数据都是保存在数组里的。当然,数组都有个大小:

若ArrayList使用无参构造函数实例化:

ArrayList<Integer> arrayList = new ArrayList<Integer>();

那么,内部数组的大小默认就是10:

1 public ArrayList() {
2     this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; //空数组
3 }
4
5 //刚实例化完,其实数组为空数组,在往arrayList添加数组时才会扩充容量(默认大小为10),扩充过程继续往后看。
6 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
7 private static final int DEFAULT_CAPACITY = 10; //默认最小容量为10

下面,研究一下add(E element)方法:

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

  其中,ensureCapacityInternal函数用来确保数组大小足够使用(至少为size+1),该函数如下:

 1 private void ensureCapacityInternal(int minCapacity) {
 2     if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { //若为空数组(实例化完成后),进入此分支
 3         minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); //minCapacity = Math.max(10, 1);
 4     }
 5
 6     ensureExplicitCapacity(minCapacity); //ensureExplicitCapacity(10);
 7 }
 8
 9 private void ensureExplicitCapacity(int minCapacity) {
10     modCount++;
11
12     // overflow-conscious code
13     if (minCapacity - elementData.length > 0) //10-0>0
14         grow(minCapacity); //grow(10); 这就是首次实例化之后的扩充容量了;
15 }
16
17 private void grow(int minCapacity) {
18     // overflow-conscious code
19     int oldCapacity = elementData.length;
20     int newCapacity = oldCapacity + (oldCapacity >> 1); //newCapacity 扩充为1.5倍
21     if (newCapacity - minCapacity < 0)
22         newCapacity = minCapacity;
23     if (newCapacity - MAX_ARRAY_SIZE > 0)
24         newCapacity = hugeCapacity(minCapacity);
25     // minCapacity is usually close to size, so this is a win:
26     elementData = Arrays.copyOf(elementData, newCapacity); //首次插数据,将容量扩充为10;之后容量不够时,数组容量按照1.5倍扩充。
27 }

可见,在首次插数据时,容量首先将数组容量扩充为10,再执行elementData[size++]=e;
在数组满了,继续插数据时,会先将容量扩充为1.5倍(10+10*1/2),再继续插数据;
当数组又满了,再继续扩充为1.5倍(15+15*1/2)......

因此,在明知数据量很大的情况下,初始化时应该指定合适大小的容量,避免arrayList反复扩充容量:

例如:若数组总容量为8000,可以这样实例化:

ArrayList<Integer> arrayList = new ArrayList<Integer>(10000);

带参构造函数如下:

public ArrayList(int initialCapacity) {
	if (initialCapacity > 0) {
		this.elementData = new Object[initialCapacity];
	}
	.....
}

  

下面研究一下add(int index, E element)

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

这个函数是将新元素插入到数组的指定位置(index),从上面的源码来看,实现过程是先将原先index位置及置换的元素向后移1位,然后再将新元素放在位置index。

  

继续研究一下remove函数的实现

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

所以,类似的实现原理:将index+1位置及之后的元素先前移动1位,再把最后一个元素清掉。

了解了ArrayList实现原理,才能更好的选择合适的容器,以及以正确的方式使用,以获取高效率。

后面将介绍另外一个List接口容器:LinkedList

原文地址:https://www.cnblogs.com/xinxinBlog/p/9926184.html

时间: 2024-12-01 19:33:12

java源码研究--ArrayList实现的相关文章

[Java源码分析]ArrayList源码分析

ArrayList是java集合中最常用的,基于一个数组实现的,容量可以动态增长. ArrayList不是现成安全的,只能在单线程环境下使用. 本文以jdk1.8的源码为例,分析其实现机制. 1.基本属性与构造函数 public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { private sta

Java源码之ArrayList

本文源码均来自Java 8 总体介绍 Collection接口是集合类的根接口,Java中没有提供这个接口的直接的实现类.Set和List两个类继承于它.Set中不能包含重复的元素,也没有顺序来存放.而List是一个有序的集合,可以包含重复的元素. 而Map又是另一个接口,它和Collection接口没有关系.Map包含了key-value键值对,同一个Map里key是不能重复的,而不同key的value是可以相同的. 在这里借用一张别人总结的对比图进行总结 集合类对比 (上图来源:http:/

Java源码之ArrayList分析

一.ArrayList简介 ArrayList底层的数据结构是数组,数组元素类型为Object类型,即可以存放所有类型数据. 与Java中的数组相比,它的容量能动态增长.当创建一个数组的时候,就必须确定它的大小,系统会在内存中开辟一块连续的空间,用来保存数组,因此数组容量固定且无法动态改变.ArrayList在保留数组可以快速查找的优势的基础上,弥补了数组在创建后,要往数组添加元素的弊端.实现的基本方法如下: 快速查找:在物理内存上采用顺序存储结构,因此可根据索引快速的查找元素. 容量动态增长:

Java源码阅读ArrayList

1简介 public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable ArrayList使用一个可变数组实现List接口,实现了List接口的所有可选操作.ArrayList除了是非线程安全的之外,其他的与Vector类似. 2成员属性 //序列化版本号 private static final lon

Java集合源码剖析——ArrayList源码剖析

ArrayList简介 ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长,类似于C语言中的动态申请内存,动态增长内存. ArrayList不是线程安全的,只能用在单线程环境下,多线程环境下可以考虑用Collections.synchronizedList(List l)函数返回一个线程安全的ArrayList类,也可以使用concurrent并发包下的CopyOnWriteArrayList类. ArrayList实现了Serializable接口,因此它支持序列化,能够通过

【源码】ArrayList源码剖析

//-------------------------------------------------------------------- 转载请注明出处:http://blog.csdn.net/chdjj by Rowandjj 2014/8/7 //-------------------------------------------------------------------- 从这篇文章开始,我将对java集合框架中的一些比较重要且常用的类进行分析.这篇文章主要介绍的是Array

如何阅读Java源码?

阅读本文大概需要 3.6 分钟. 阅读Java源码的前提条件: 1.技术基础 在阅读源码之前,我们要有一定程度的技术基础的支持. 假如你从来都没有学过Java,也没有其它编程语言的基础,上来就啃<Core Java>,那样是很难有收获的,尤其是<深入Java虚拟机>这类书,或许别人觉得好,但是未必适合现在的你. 比如设计模式,许多Java源码当中都会涉及到.再比如阅读Spring源码的时候,势必要先对IOC,AOP,Java动态代理等知识点有所了解. 2.强烈的求知欲 强烈的求知欲

如何阅读Java源码 阅读java的真实体会

刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动. 源码阅读,我觉得最核心有三点:技术基础+强烈的求知欲+耐心. 说到技术基础,我打个比方吧,如果你从来没有学过Java,或是任何一门编程语言如C++,一开始去啃<Core Java>,你是很难从中吸收到营养的,特别是<深入Java虚拟机>这类书,别人觉得好,未必适合现在的你. 虽然Tomcat的源码很漂亮,但我绝不建议你一开始就读它.我文中会专门谈到这个,暂时不展开. 强烈

Java源码阅读的真实体会

原文:http://zwchen.iteye.com/blog/1154193 刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动. 源码阅读,我觉得最核心有三点:技术基础+强烈的求知欲+耐心. 说到技术基础,我打个比方吧,如果你从来没有学过Java,或是任何一门编程语言如C++,一开始去啃<Core Java>,你是很难从中吸收到营养的,特别是<深入Java虚拟机>这类书,别人觉得好,未必适合现在的你. 虽然Tomcat的