JDK源码学习系列04----ArrayList

1.ArrayList简介

ArrayList是基于Object[] 数组的,也就是我们常说的动态数组。它能很方便的实现数组的增加删除等操作。

public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable</span>

ArrayList支持泛型,它继承自AbstractList,实现了List、RandomAccess、Cloneable、java.io.Serializable接口。

List接口定义了列表必须实现的方法。

RandomAccess是一个标记接口,接口内没有定义任何内容。

实现了Cloneable接口的类,可以调用Object.clone方法返回该对象的浅拷贝。

通过实现 java.io.Serializable 接口以实现序列化功能。

2.ArrayList成员变量

private transient Object[] elementData;//注意关键字 transient
private int size;//实际size,不是容量

Java关键字 transient:是为了在序列化时保护对象的某些域不被序列化,在Java序列化Serializable详解中有所提及。关于transient的详细内容稍后补上。

3.ArrayList构造函数

 public ArrayList(int initialCapacity) {//为ArrayList初始化容量
super();
if (initialCapacity < 0)//若传入参数小于0,则报IllegalArgumentException的错误
throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
this.elementData = new Object[initialCapacity];//初始elementData数组的大小为此传入的参数
} public ArrayList() {//ArrayList初始容量,即默认容量为10
this(10);
} public ArrayList(Collection<? extends E> c) {//把整个集合初始化给ArrayList.注意:该集合内的数据类型必须与ArrayList一致!!!!
elementData = c.toArray();
size = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)//!!这是jdk的一个bug,说是c.toArray()不一定返回的是Object类型
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);//调用的Arrays.copyOf()
}

4.ArrayList的成员函数

a.void trimToSize() 

public void trimToSize() {//节约内存
modCount++;//此变量记录ArrayList被改变的次数 !!!超级注意!!!
int oldCapacity = elementData.length;
if (size < oldCapacity) {//!!size往往不等于elementData.length;elementData.length是数组的初始长度,size是实际内容的长度!!
elementData = Arrays.copyOf(elementData, size);
}
}

modCount变量是记录ArrayList被改变的次数。为什么需要这个变量呢?

ArrayList不是线程安全(异步)的。这里会引出Fail-Fast机制: 

ArrayList不是线程安全的,因此如果在使用迭代器的过程中有其他线程修改了map,那么将抛出ConcurrentModificationException,这就是所谓fail-fast策略。

这一策略在源码中的实现是通过modCount域,modCount顾名思义就是修改次数,对ArrayList 结构的修改(长度的变化,增加,删除;赋值不是结构变化)都将增加这个值,那么在迭代器初始化过程中会将这个值赋给迭代器的expectedModCount。在迭代过程中,判断modCount跟expectedModCount是否相等,如果不相等就表示已经有其他线程修改了ArrayList。

ArrayList中的mouCount是在他的父类Abstract中申明的。

protected transient int modCount = 0;

b.void ensureCapacity(int minCapacity)

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;//若扩为 1.5*原容量+1 后还是小于传入的参数,则把传入的参数作为新容量
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
}

ArrayList需要扩容时至少都是扩为 1.5*原容量+1 ,若1.5*原容量+1 还是小于传入的参数,才把传入的参数作为新容量。

c.boolean contains(Object o)

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

d.int indexOf(Object o)

 public int indexOf(Object o) {
if (o == null) {//定位是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;
}

e.Object[] toArray()   /   <T> T[] toArray(T[] a)

调用的是Arrays.copyOf()

public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
 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());//若传入的数组长度小于size(ArrayList实际长度),则返回一个长度为size新的数组
System.arraycopy(elementData, 0, a, 0, size);//若传入数组长度相等,则把elementData复制进传入数组
if (a.length > size)//若传入的a的长度大于原本数组,则后面补null
a[size] = null;
return a;
}

f.E set(int index, E element)

set是直接替换掉该位置元素;而add是插入该位置,其余元素后移。

public E set(int index, E element) {
RangeCheck(index);//参数检查的方法~~一定要时刻注意参数检查哦~~
E oldValue = (E) elementData[index];
elementData[index] = element;
return oldValue;
}

g.boolean add(E e)   /   void add(int index, E element)

public boolean add(E e) {
ensureCapacity(size + 1); // add()时先扩容!
elementData[size++] = e;//!!写的很好,size++的同时还完成了在最后位置的赋值。之所以size++不会报边界溢出的错误是因为上面已经扩容了。
return true;
}
public void add(int index, E element) {
if (index > size || index < 0)throw new IndexOutOfBoundsException("Index: "+index+", Size: "+size);
ensureCapacity(size+1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,size - index);//!!把elementData的数据从index->末尾全部复制到从index+1开始,复制长度无size-index
elementData[index] = element;//相当于把传入的element插入到空出的位置,即原index
size++;
}

注意System.arracopy()方法!!

h.E remove(int index)    /    boolean remove(Object o)    /    void fastRemove(int index)

 public E remove(int index) {
RangeCheck(index);
modCount++;
E oldValue = (E) elementData[index];//得到需要返回的被remove掉的元素
int numMoved = size - index - 1;//复制时复制的长度,
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,numMoved);//把原数组从index+1-->末尾的数据复制到 index的位置,即相当于把原本index上的数据覆盖掉了,这样最后就空出了一个位置。
elementData[--size] = null; // 先把size减一,在把最后一赋值为null
return oldValue;
}
public boolean remove(Object o) {
if (o == null) {//!!判断是否为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) {//快速移除! 此方法跳过了边界检查这一步,且不会返回被移除的元素。平时还是不要用哦~~
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
}

i.void clear()

 public void clear() {//把每个都置为null,再设置size=0;
modCount++;
// Let gc do its work
for (int i = 0; i < size; i++)
elementData[i] = null; size = 0;
}

g.boolean addAll(int index, Collection<? extends E> c)

public boolean addAll(int index, Collection<? extends E> c) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacity(size + numNew); // 扩容时传入的参数为:现在的实际长度+传入的集合的长度
int numMoved = size - index;
if (numMoved > 0)
System.arraycopy(elementData, index, elementData, index + numNew,numMoved);//把原数组从index-->末尾复制到 index+numNew-->末尾,即中间空出numNum的长度来为传入的集合做准备。
System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}

5.总结

a.ArrayList动态数组!

b.ArrayList 非线程安全,即 是异步的。 单线程才用ArrayList。

JDK源码学习系列04----ArrayList的更多相关文章

  1. JDK源码学习系列05----LinkedList

                                             JDK源码学习系列05----LinkedList 1.LinkedList简介 LinkedList是基于双向链表实 ...

  2. JDK源码学习系列03----StringBuffer+StringBuilder

                         JDK源码学习系列03----StringBuffer+StringBuilder 由于前面学习了StringBuffer和StringBuilder的父类A ...

  3. JDK源码学习系列02----AbstractStringBuilder

     JDK源码学习系列02----AbstractStringBuilder 因为看StringBuffer 和 StringBuilder 的源码时发现两者都继承了AbstractStringBuil ...

  4. JDK源码学习系列01----String

                                                     JDK源码学习系列01----String 写在最前面: 这是我JDK源码学习系列的第一篇博文,我知道 ...

  5. JDK源码学习笔记——Integer

    一.类定义 public final class Integer extends Number implements Comparable<Integer> 二.属性 private fi ...

  6. 源码学习系列之SpringBoot自动配置(篇一)

    源码学习系列之SpringBoot自动配置源码学习(篇一) ok,本博客尝试跟一下Springboot的自动配置源码,做一下笔记记录,自动配置是Springboot的一个很关键的特性,也容易被忽略的属 ...

  7. 源码学习系列之SpringBoot自动配置(篇二)

    源码学习系列之SpringBoot自动配置(篇二)之HttpEncodingAutoConfiguration 源码分析 继上一篇博客源码学习系列之SpringBoot自动配置(篇一)之后,本博客继续 ...

  8. SpringBoot源码学习系列之SpringMVC自动配置

    目录 1.ContentNegotiatingViewResolver 2.静态资源 3.自动注册 Converter, GenericConverter, and Formatter beans. ...

  9. Spring5.0源码学习系列之浅谈BeanFactory创建

    Spring5.0源码学习系列之浅谈BeanFactory创建过程 系列文章目录 提示:Spring源码学习专栏链接 @ 目录 系列文章目录 博客前言介绍 一.获取BeanFactory主流程 二.r ...

随机推荐

  1. sqlserver自学笔记之的流程控制语句

    1.使用if else语句 ①在学生表中,查找名字为王刚的同学,如果存在,显示该同学的信息,否则显示查无此人 if exists(select sno from student where sname ...

  2. 一个网友写的栈,问为啥不能迭代。具有__iter__ 和next方法的对象叫迭代器-七七巴巴黄页网

    一个网友写的栈,问为啥不能迭代.具有__iter__ 和next方法的对象叫迭代器-七七巴巴黄页网 一个网友写的栈,问为啥不能迭代.具有__iter__ 和next方法的对象叫迭代器 python视频 ...

  3. 《Getting Started with WebRTC》第一章 WebRTC介绍

    <Getting Started with WebRTC>第一章 WebRTC介绍 ​ 本章是对WebRTC做概念性的介绍. 阅读完本章后.你将对下面方面有一个清晰的理解:   .  什么 ...

  4. ThinkPHP中实例化对象M()和D()的区别,select和find的区别

    原文:ThinkPHP中实例化对象M()和D()的区别,select和find的区别 1.ThinkPHP中实例化对象M()和D()的区别 在实例化的过程中,经常使用D方法和M方法,这两个方法的区别在 ...

  5. 15个最好的HTML5前端响应式框架(2014)

    文中的多个框架基于SASS创建,SCSS是一种比LESS更简洁的样式表编程语言,它能够编绎成CSS,可复用CSS代码,声明变量,甚至是函数,类Ruby/Python的语法.參见: LESS vs SA ...

  6. YUV格式具体解释

    YUV是指亮度參量和色度參量分开表示的像素格式,而这样分开的优点就是不但能够避免相互干扰,还能够减少色度的採样率而不会对图像质量影响太大.YUV是一个比較笼统地说法,针对它的详细排列方式,能够分为非常 ...

  7. [Cocos2d-x]CCSpriteBatchNode的使用

    文档: http://cocos2d.cocoachina.com/document/index/class?url=dd/d95/classcocos2d_1_1_c_c_sprite_batch_ ...

  8. SQL语句查询数据库的触发器、存储过程、视图以及表的SQL语句

    Sql Server数据库用SQL语句查询方法如下: select name from sysobjects where xtype='TR' --所有触发器 select name from sys ...

  9. MooTools,jQuery库的一些比对

    jQuery与MooTools库的一些比对   今天就我自己的一些认识比对下这两个JS框架,更多的是希望大家能够对MooTools这个JS框架有更多的认识.毕竟,大多数从事web前端的人对上手容易的j ...

  10. 使用Ambari快速部署Hadoop大数据环境

    使用Ambari快速部署Hadoop大数据环境   发布于2013-5-24   前言 做大数据相关的后端开发工作一年多来,随着Hadoop社区的不断发展,也在不断尝试新的东西,本文着重来讲解下Amb ...