一、ArrayList 定义

ArrayList 是一个用数组实现的集合,支持随机访问,元素有序且可以重复。

①、实现 List 接口

List接口继承Collection接口,是List类的顶层接口,定义了大量方法,子类可进行个性化实现

②、实现RandomAccess接口

RandomAccess 接口是一个标记接口,类似我们熟悉的Serializable接口,表明支持随机访问,在工具类Collections中有发挥其作用。

③、实现 Cloneable 接口

能否调用Object.clone() 方法的关键,如果未实现,调用clone则抛出CloneNoSupportException异常。

④、实现 Serializable 接口

这个接口没什么好说的,能都进行序列化的关键。

二、字段属性

ArrayList类的主要属性如下:

//Arraylist默认初始大小
private static final int DEFAULT_CAPACITY = 10;
//空的数组实例
private static final Object[] EMPTY_ELEMENTDATA = {};
//这也是一个空的数组实例,与上面空数组的区别在于,当第一个元素添加的时候,需要膨胀多少
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//存储 ArrayList集合的元素的数组
transient Object[] elementData;
//ArrayList集合元素个数
private int size;

三、构造函数

1.无参构造函数,构造一个空的数组。

2.有参构造函数,参数指定数组的初始大小。构造给定的初始大小的数组。

 3.有参构造函数,参数是一个集合,最终是将给定的集合复制到数组中。

四、主要方法

1.添加元素

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

在添加元素之前,需要调用ensureCapacityInternal()方法来确定数组的大小,保证数组有足够的大小来容量新增的元素。如果数组已经满了,则进行数组增大,主要是借助Arrays.copyOf()方法实现的。

 private void ensureExplicitCapacity(int minCapacity) {
modCount++; // overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
} 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);
}

①、当通过 ArrayList() 构造一个空集合,初始长度是为0的,第 1 次添加元素,会创建一个长度为10的数组,并将该元素赋值到数组的第一个位置。

②、第 2 次添加元素,集合不为空,而且由于集合的长度size+1是小于数组的长度10,所以直接添加元素到数组的第二个位置,不用扩容。

③、第 11 次添加元素,此时 size+1 = 11,而数组长度是10,这时候创建一个长度为10+10*0.5 = 15 的数组(扩容1.5倍),然后将原数组元素引用拷贝到新数组。并将第 11 次添加的元素赋值到新数组下标为10的位置。

数组的最大长度为 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

2.删除元素

①、通过数组索引删除元素

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

首先判断索引的值是否大于等于size,超过集合的大小则抛出IndexOutOfBoundsException异常,再通过System.arraycopy()方法对数组进行复制操作,最终形成的数组是删除指定位置后的数组,达到了移动数组中元素位置的效果。

②、直接删除指定元素

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

删除指定元素的逻辑非常简单,即首先遍历数组,找到指定元素在数组中的位置后,最终的逻辑和通过索引删除元素一致。

3.查找元素

 public E get(int index) {
rangeCheck(index); return elementData(index);
}

查找元素是根据索引查找,即通过索引获取数组中的值。

4.集合遍历

①、普通 for 循环遍历,通过循环,在借助get方法即可遍历ArrayList集合。

②、通过terator遍历

首先获取迭代器,其返回的是ArrayList类中自定义的内部类Itr。

public Iterator<E> iterator() {
return new Itr();
}
 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;
} @SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
} 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();
}
} @Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
} final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}

③、forEach遍历ArrayList本质即通过迭代器遍历。

5.toArray,ArrayList集合转换成数组

public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}

6.trimToSize()

public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}

这个方法主要是释放内存,将ArrayList集合中数组的大小调整为size的大小。

7.subList方法

这个方法往往引起我们犯错,subList返回的并不是一个ArrayList。经常我们容易使用subList后,再使用add方法,然后报异常。具体原因源码显示的很清楚。

public List<E> subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);
return new SubList(this, 0, fromIndex, toIndex);
}

其他方法的学习省略,感兴趣的可以参考源码。

五、总结

  • ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长,类似于C语言中的动态申请内存,动态增长内存。
  • ArrayList不是线程安全的,只能用在单线程环境下,多线程环境下可以考虑用Collections.synchronizedList(List l)函数返回一个线程安全的ArrayList类,也可以使用concurrent并发包下的CopyOnWriteArrayList类。
  • ArrayList的实现中大量地调用了Arrays.copyof()和System.arraycopy()方法
  • ArrayList基于数组实现,可以通过下标索引直接查找到指定位置的元素,因此查找效率高,但每次插入或删除元素,就要大量地移动元素,插入删除元素的效率低。
  • ArrayList中允许元素为null,且可以重复。

Java 集合学习--ArrayList的更多相关文章

  1. 2019/3/4 java集合学习(二)

    java集合学习(二) 在学完ArrayList 和 LinkedList之后,基本已经掌握了最基本的java常用数据结构,但是为了提高程序的效率,还有很多种特点各异的数据结构等着我们去运用,类如可以 ...

  2. 【源码阅读】Java集合之一 - ArrayList源码深度解读

    Java 源码阅读的第一步是Collection框架源码,这也是面试基础中的基础: 针对Collection的源码阅读写一个系列的文章,从ArrayList开始第一篇. ---@pdai JDK版本 ...

  3. Java集合学习(9):集合对比

    一.HashMap与HashTable的区别 HashMap和Hashtable的比较是Java面试中的常见问题,用来考验程序员是否能够正确使用集合类以及是否可以随机应变使用多种思路解决问题.Hash ...

  4. 从源码看Java集合之ArrayList

    Java集合之ArrayList - 吃透增删查改 从源码看初始化以及增删查改,学习ArrayList. 先来看下ArrayList定义的几个属性: private static final int ...

  5. 转:深入Java集合学习系列:HashSet的实现原理

    0.参考文献 深入Java集合学习系列:HashSet的实现原理 1.HashSet概述: HashSet实现Set接口,由哈希表(实际上是一个HashMap实例)支持.它不保证set 的迭代顺序:特 ...

  6. 2019/3/2周末 java集合学习(一)

    Java集合学习(一) ArraysList ArraysList集合就像C++中的vector容器,它可以不考虑其容器的长度,就像一个大染缸一 样,无穷无尽的丢进去也没问题.Java的数据结构和C有 ...

  7. java集合学习(2):Map和HashMap

    Map接口 java.util 中的集合类包含 Java 中某些最常用的类.最常用的集合类是 List 和 Map. Map 是一种键-值对(key-value)集合,Map 集合中的每一个元素都包含 ...

  8. Java集合学习(7):ArrayList

    一.概述 ArrayList可以理解为动态数组,就是Array的复杂版本.与Java中的数组相比,它的容量能动态增长.ArrayList是List接口的可变数组的实现.实现了所有可选列表操作,并允许包 ...

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

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

随机推荐

  1. ROS编译工作区缺少cv_bridge的问题解决

    cv_bridge是OpenCV与ROS之间的格式转换桥梁,编译工作区时遇到报错(目标不存在),直接将cv_bridge包复制到指定的目录即可. 下载地址:https://github.com/ros ...

  2. asp.net中Page.ClientScript.RegisterStartupScript用法小结

    ClientScript.RegisterStartupScript(Page.GetType(), "Show", "alert('" + l_strConf ...

  3. 深度优先搜索(深搜)——Deep First Search【例题:迷宫】

           深度优先搜索 基本思想:先选择一种可能情况向前探索,在探索过程中,一点那发现原来的选择是错误的,就退回一步重新选择,继续向前探索,(回溯)反复进行. [例题]迷宫问题           ...

  4. hadoop 错误

    错误:DataXceiver error processing WRITE_BLOCK operation 2014-05-06 15:21:30,378 ERROR org.apache.hadoo ...

  5. 18年selenium3+python3+unittest自动化测试教程(上)

    第一章 自动化测试课程介绍和课程大纲 1.自动化测试课程介绍 简介:讲解什么是自动化测试和课程大纲讲解,课程需要的基础和学后的水平 python3.7+selenium3 pycharm 第二章自动化 ...

  6. Windows 2008 server + IIS 7 设置身份模拟(ASP.NET impersonation)

    IIS7 与 IIS 6 相比有了很大的改动,原来在 IIS 6 下可以的设置到了 IIS 7 下有的会发生变化.身份模拟的配置上,IIS7 和 IIS6有很大不同,网上IIS6的身份模拟的文章比较多 ...

  7. 自定义App首次启动引导页

    代码如下 #import"ZBGuidePageView.h" @interfaceZBGuidePageView()<UIScrollViewDelegate> @p ...

  8. NEC css规范

    CSS规范 - 分类方法 SS文件的分类和引用顺序 通常,一个项目我们只引用一个CSS,但是对于较大的项目,我们需要把CSS文件进行分类. 我们按照CSS的性质和用途,将CSS文件分成“公共型样式”. ...

  9. Spring初始介绍

    一.spring介绍 三层架构中spring位置: spring:对象的容器,相当于map容器,已经存好了相应的对象,而这三层对象(web层,service层,)进行创建时,不需要在进行new对象,s ...

  10. Flask中异常捕获

    HTTP 异常主动抛出 abort 方法 抛出一个给定状态代码的 HTTPException 或者 指定响应,例如想要用一个页面未找到异常来终止请求,你可以调用 abort(404). 参数: cod ...