版权声明:本文为博主原创文章,转载请注明出处,欢迎交流学习!

ArrayList底层维护的是一个动态数组,每个ArrayList实例都有一个容量。该容量是指用来存储列表元素的数组的大小。它总是至少等于列表的大小。随着向 ArrayList 中不断添加元素,其容量也自动增长。

ArrayList不是同步的(也就是说不是线程安全的),如果多个线程同时访问一个ArrayList实例,而其中至少一个线程从结构上修改了列表,那么它必须保持外部同步,在多线程环境下,可以使用Collections.synchronizedList方法声明一个线程安全的ArrayList,例如:

List arraylist = Collections.synchronizedList(new ArrayList());

下面通过ArrayList的源码来分析其原理。

1、ArrayList的构造方法:ArrayList提供了三种不同的构造方法

1) ArrayList(),构造一个初始容量为 10 的空列表。

2) ArrayList(int initialCapacity),构造一个具有指定初始容量的空列表。

3) ArrayList(Collection<? extends E> c),构造一个包含指定 collection 的元素的列表,这些元素是按照该 collection 的迭代器返回它们的顺序排列的。

源码如下:

  

  private transient Object[] elementData;
  public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
this.elementData = new Object[initialCapacity]; //生成一个长度为10的Object类型的数组
} public ArrayList() {
this(10); //调用ArrayList(int i)
} public ArrayList(Collection<? extends E> c) {
elementData = c.toArray(); //返回包含此 collection 中所有元素的数组
size = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class); //复制指定的数组,返回包含相同元素和长度的Object类型的数组
}

  当采用不带参数的构造方法ArrayList()生成一个集合对象时,其实是在底层调用ArrayList(int initialCapacity)这一构造方法生产一个长度为10的Object类型的数组。当采用带有集合类型参数的构造方法时,在底层生成一个包含相同的元素和长度的Object类型的数组。

2、add方法:ArrayList提供了两种添加元素的add方法

1) add(E e),将指定的元素添加到此列表的尾部。

2) add(int index, E e),将指定的元素插入此列表中的指定位置。向右移动当前位于该位置的元素(如果有)以及所有后续元素(将其索引加 1)。

public boolean add(E e) {
ensureCapacity(size + 1); // 扩大数组容量
elementData[size++] = e; //将元素e添加到下标为size的Object数组中,并且执行size++
return true;
}
public void add(int index, E element) {
if (index > size || index < 0) //如果指定要插入的数组下标超过数组容量或者指定的下标小于0,抛异常
throw new IndexOutOfBoundsException("Index: "+index+", Size: "+size); ensureCapacity(size+1); // 扩大数组容量
System.arraycopy(elementData, index, elementData, index + 1,size - index); //从指定源数组中复制一个数组,复制从指定的位置开始,到目标数组的指定位置结束。
// elementData --- 源数组 index --- 源数组中的起始位置
// elementData --- 目标数组 index+1 --- 目标数组中的起始位置
// size - index --- 要复制的数组元素的数量
elementData[index] = element; //将要添加的元素放到指定的数组下标处
size++;
}
 public void ensureCapacity(int minCapacity) {
modCount++;
int oldCapacity = elementData.length; //原数组的容量
if (minCapacity > oldCapacity) {
Object oldData[] = elementData;
int newCapacity = (oldCapacity * 3)/2 + 1; //定义新数组的容量,为原数组容量的1.5倍+1
if (newCapacity < minCapacity)
newCapacity = minCapacity;
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity); //复制指定的数组,返回新数组的容量为newCapacity
}
}

  如果集合中添加的元素超过了10个,那么ArrayList底层会新生成一个数组,长度为原数组的1.5倍+1,并将原数组中的元素copy到新数组中,并且后续添加的元素都会放在新数组中,当新数组的长度无法容纳新添加的元素时,重复该过程。这就是集合添加元素的实现原理。

3、get方法:

1) get(int index),返回此列表中指定位置上的元素。

public E get(int index) {
RangeCheck(index); //检查传入的指定下标是否合法 return (E) elementData[index]; //返回数组下标为index的数组元素
} private void RangeCheck(int index) {
if (index >= size) //如果传入的下标大于或等于集合的容量,抛异常
throw new IndexOutOfBoundsException(
"Index: "+index+", Size: "+size);
}

  

4、remove方法:

1) E remove(int index),移除此列表中指定位置上的元素。向左移动所有后续元素(将其索引减 1)。

2) boolean remove(Object o),移除此列表中首次出现的指定元素(如果存在)。如果列表不包含此元素,则列表不做改动,返回boolean值。

public E remove(int index) {
RangeCheck(index); //检查指定的下标是否合法 modCount++;
E oldValue = (E) elementData[index]; //获取指定下标的数组元素 int numMoved = size - index - 1; //要移动的元素个数
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index, numMoved); //移动数组元素
elementData[--size] = null; // Let gc do its work return oldValue;
} public boolean remove(Object o) {
if (o == null) { //如果传入的参数为null
for (int index = 0; index < size; index++)
if (elementData[index] == null) { //移除首次出现的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) { //移除指定位置的元素,实现方法类似remove(int i)
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // Let gc do its work
}

 

5、clone方法:

1) Object clone(),返回此ArrayList实例的浅表副本(不复制这些元素本身) 。

 public Object clone() {
try {
ArrayList<E> v = (ArrayList<E>) super.clone(); //调用Object类的clone方法返回一个ArrayList对象
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();
}
}

  

以上通过对ArrayList部分关键源码的分析,知道了ArrayList底层的实现原理,关于ArrayList源码有以下几点几点总结:

1) ArrayList 底层是基于数组来实现的,可以通过下标准确的找到目标元素,因此查找的效率高;但是添加或删除元素会涉及到大量元素的位置移动,效率低。

2) ArrayList提供了三种不同的构造方法,无参数的构造方法默认在底层生成一个长度为10的Object类型的数组,当集合中添加的元素个数大于10,数组会自动进行扩容,即生成一个新的数组,并将原数组的元素放到新数组中。

3) ensureCapacity方法对数组进行扩容,它会生成一个新数组,长度是原数组的1.5倍+1,随着向ArrayList中不断添加元素,当数组长度无法满足需要时,重复该过程。

Java集合框架之一:ArrayList源码分析的更多相关文章

  1. java集合系列之ArrayList源码分析

    java集合系列之ArrayList源码分析(基于jdk1.8) ArrayList简介 ArrayList时List接口的一个非常重要的实现子类,它的底层是通过动态数组实现的,因此它具备查询速度快, ...

  2. java集合框架03——ArrayList和源码分析

    最近忙着替公司招人好久没写了,荒废了不好意思. 上一章学习了Collection的架构,并阅读了部分源码,这一章开始,我们将对Collection的具体实现进行详细学习.首先学习List.而Array ...

  3. Java集合系列[1]----ArrayList源码分析

    本篇分析ArrayList的源码,在分析之前先跟大家谈一谈数组.数组可能是我们最早接触到的数据结构之一,它是在内存中划分出一块连续的地址空间用来进行元素的存储,由于它直接操作内存,所以数组的性能要比集 ...

  4. java集合系列之LinkedList源码分析

    java集合系列之LinkedList源码分析 LinkedList数据结构简介 LinkedList底层是通过双端双向链表实现的,其基本数据结构如下,每一个节点类为Node对象,每个Node节点包含 ...

  5. Java集合系列[4]----LinkedHashMap源码分析

    这篇文章我们开始分析LinkedHashMap的源码,LinkedHashMap继承了HashMap,也就是说LinkedHashMap是在HashMap的基础上扩展而来的,因此在看LinkedHas ...

  6. Java集合系列:-----------03ArrayList源码分析

    上一章,我们学习了Collection的架构.这一章开始,我们对Collection的具体实现类进行讲解:首先,讲解List,而List中ArrayList又最为常用.因此,本章我们讲解ArrayLi ...

  7. java集合框架04——LinkedList和源码分析

    上一章学习了ArrayList,并分析了其源码,这一章我们将对LinkedList的具体实现进行详细的学习.依然遵循上一章的步骤,先对LinkedList有个整体的认识,然后学习它的源码,深入剖析Li ...

  8. 【java集合总结】-- ArrayList源码解析

    一.前言 要想深入的了解集合就必须要通过分析源码来了解它,那如何来看源码,要看什么东西呢?主要从三个方面: 1.看继承结构 看这个类的继承结构,处于一个什么位置,不需要背记,有个大概的感觉就可以,我自 ...

  9. Java集合:HashSet的源码分析

    Java集合---HashSet的源码分析   一.  HashSet概述: HashSet实现Set接口,由哈希表(实际上是一个HashMap实例)支持.它不保证set 的迭代顺序:特别是它不保证该 ...

  10. Java集合系列[3]----HashMap源码分析

    前面我们已经分析了ArrayList和LinkedList这两个集合,我们知道ArrayList是基于数组实现的,LinkedList是基于链表实现的.它们各自有自己的优劣势,例如ArrayList在 ...

随机推荐

  1. JavaScript 下拉框 左边添加至右边

    关于如何实现右边下拉框中选项的排序一时没有好的解决方法,等想到了回来补充 <!DOCTYPE html> <html> <head> <meta charse ...

  2. for 没有作用域的说话

    for i in range(10): passprint(i) 打印的结果就是9 打印的最后一次结果

  3. ---mingw Linux交叉编译给Window的工具

    https://arrayfire.com/cross-compile-to-windows-from-linux/

  4. 一个nginx 回源限速的bug处理过程记录

    一个生产环境,nginx占用cpu很高. top - :: up day, :, users, load average: 13.26, 13.20, 13.20 Tasks: total, runn ...

  5. C语言实现的反转字符串

    这段代码大约是10年前写的了,一直收藏在自己的代码仓库里面,贴出来分享下. 网上也有很多类似的代码,学生们用的比较多,工作中用的很少,权做参考. char* ReverseString(char* s ...

  6. javaMail实现收发邮件(二)

    JavaMail API常用类 JavaMail API使用javax.mail.Message类来表示一封邮件,Message类是一个抽象类,所以我们需要使用其子类javax.mail.intern ...

  7. Spring MVC请求处理流程

    从web.xml中 servlet的配置开始, 根据servlet拦截的url-parttern,来进行请求转发   Spring MVC工作流程图   图一   图二    Spring工作流程描述 ...

  8. java 导mysql数据为表格给浏览器接收

    jar 包准备 <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</a ...

  9. [leetcode]88. Merge Sorted Array归并有序数组

    Given two sorted integer arrays nums1 and nums2, merge nums2 into nums1 as one sorted array. Note: T ...

  10. Spring Boot+Quartz实现一个实时管理的定时任务

    转载 https://www.cnblogs.com/wujiwen/p/9615120.html 项目实践过程中碰到一个动态管理定时任务的需求:针对每个人员进行信息的定时更新,具体更新时间可随时调整 ...