GitHub Page: http://blog.cloudli.top/posts/Java-ArrayList/

ArrayList 继承于 AbstractList ,实现了 List、RandomAccess、Cloneable、Serializable 接口。

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;

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
  • DEFAULT_CAPACITY:默认容量为 10。
  • EMPTY_ELEMENTDATA:通过构造方法指定的容量为 0 时,使用该空数组。
  • DEFAULTCAPACITY_EMPTY_ELEMENTDATA:调用无参构造方法时,使用该数组。
  • elementData:保存添加的元素,在 ArrayList 被序列化时,该属性不会被序列化。
  • size:ArrayList 保存的元素个数。
  • MAX_ARRAY_SIZE:ArrayList 能够容纳的最大长度,231 - 1 - 8。

ArrayList 的构造方法

  • ArrayList():构造一个容量为 10 的空列表。
  • ArrayList(int initialCapacity):构造一个指定容量的空列表。
  • ArrayList(Collection<? extends E> c):构造一个列表,将给定集合中的元素按照迭代器返回的顺序复制到列表中。

使用无参构造方法创建 ArrayList 时,elementData 的长度是 0,当第一次添加元素时,它会扩容到 DEFAULT_CAPACITY

使用指定容量构造 ArrayList 时,elementData 的长度为指定的长度。

ArrayList()

/**
* Constructs an empty list with an initial capacity of ten.
* 构造一个容量为 10 的空列表,当第一次添加元素时,elementData 会扩容到 10。
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

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 指定容量 < 0 时抛出异常
*/
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);
}
}

ArrayList(Collection<? extends E> c)

/**
* Constructs a list containing the elements of the specified
* collection, in the order they are returned by the collection's
* iterator.
* 构造一个列表,并将给定的集合中的元素按照迭代器返回的顺序复制到列表中。
*
* @param c the collection whose elements are to be placed into this list 将被复制到列表中的元素的集合
* @throws NullPointerException if the specified collection is null 集合为空时抛出异常
*/
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}

集合转换为数组后,如果不是 Object[] 类型,会使用 Arrays.copyOf 方法返回一个新的 Object 数组。

元素操作的基本方法

方法名 说明 时间复杂度
E get(int index) 返回指定索引的元素 O(1)
E set(int index, E element) 替换指定索引的元素,返回旧元素 O(1)
boolean add(E e) 向列表的末尾添加元素 O(1)
void add(int index, E element) 在指定位置插入元素 O(N)
E remove(int index) 删除指定位置的元素,返回旧元素 O(N)
boolean remove(Object o) 删除列表中与给定对象相同的元素 O(N)
boolean contains(Object o) 判断列表中是否包含指定元素 O(N)
int indexOf(Object o) 返回指定元素在列表中的索引 O(N)

其中,contains 方法直接 返回 indexOf(o) >= 0 。

对列表操作的方法

clear()

清空列表,将所有元素的引用指向 null,等待垃圾回收器回收,此操作不减小数组容量。

public void clear() {
modCount++; // clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null; size = 0;
}

subList(int fromIndex, int toIndex)

返回列表的指定区域(子列表),对返回的列表做修改会影响整个列表。

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

迭代器方法

iterator()

返回一个迭代器,如果在迭代器遍历过程中调用了列表的 add,remove 等方法,会抛出 ConcurrentModificationException

public Iterator<E> iterator() {
return new Itr();
}

listIterator()

返回一个迭代器,该迭代器可以向前后两个方向遍历元素。ListIter 继承于 Iter 。

public ListIterator<E> listIterator() {
return new ListItr(0);
}

listIterator(int index)

返回一个从指定位置开始的迭代器。

public ListIterator<E> listIterator(int index) {
if (index < 0 || index > size)
throw new IndexOutOfBoundsException("Index: "+index);
return new ListItr(index);
}

扩容方法

ArrayList 有两个与扩容有关的方法:

  • void grow(int minCapacity)
  • int hugeCapacity(int minCapacity)

grow(int 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);
}

minCapacity 为添加新元素后数组的长度(此时还未添加)。

首先将新长度(newCapacity)设置为原来的 1.5 倍,然后计算新的长度是否能够达到 minCapacity,如果不能,则把新长度设置为 minCapacity 的大小。

如果新长度比 ArrayList 能够容纳的最大长度还要大,调用 hugeCapacity 方法来计算。否则,将列表长度调整为新长度。

hugeCapacity(int minCapacity)

private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}

这里为什么要判断 minCapacity < 0 ?

假如列表中已经有 oldCapacity = 231 - 1 个元素,在添加新元素的时候,minCapacity 为原来的长度 + 1,溢出变为 minCapacity = -231,newCapacity 扩大到 1.5 倍也溢出为负数(绝对值 < minCapacity),此时 grow 方法中 newCapacity - minCapacity > 0(绝对值小的负数 - 绝对值大的负数),最后调用该方法时传递的 minCapacity 就是 -231,将抛出异常。

最后如果 minCapacity 大于 ArrayList 能容纳的最大长度,就返回整型最大值,否则返回 MAX_ARRAY_SIZE

函数式接口方法

ArrayList 有 4 个参数为函数式接口的方法:

1. forEach(Consumer<? super E> action)

消费列表中的元素,可以对列表中的元素作出处理。以下代码打印列表中所有的偶数:

ArrayList<Integer> list = ...;

list.forEach(e -> {
if (e % 2 == 0)
System.out.printf("%d ", e);
});

2. removeIf(Predicate<? super E> filter)

删除列表中满足给定条件的元素。以下代码删除列表中所有偶数元素:

list.removeIf(e -> {
return e % 2 == 0;
});

也可以这样写:

list.removeIf(e -> e % 2 == 0);

3. replaceAll(UnaryOperator operator)

替换列表中满足给定条件的元素。以下代码将所有偶数元素替换为 0 :

list.replaceAll(e -> {
return e % 2 == 0 ? 0 : e;
});

也可以这样写:

list.replaceAll(e -> e % 2 == 0 ? 0 : e);

4. sort(Comparator<? super E> c)

使用给定的规则对列表元素排序。如果元素的类型已经实现了 Comparable 接口,可以直接传递方法引用。

以下代码对列表进行升序排序:

list.sort(Integer::compareTo);

逆序可以使用 Comparator.reverseOrder :

list.sort(Comparator.reverseOrder());

也可以在 lambda 表达式中写判断逻辑。

Java 8 ArrayList 详解的更多相关文章

  1. Java.util.ArrayList详解

    java.util.ArrayList就是传说中的动态数组. 继承了关系,有此可看出ArrayList与list的collection的关系 public class ArrayList<E&g ...

  2. 转:Java HashMap实现详解

    Java HashMap实现详解 转:http://beyond99.blog.51cto.com/1469451/429789 1.    HashMap概述:    HashMap是基于哈希表的M ...

  3. java集合框架详解

    java集合框架详解 一.Collection和Collections直接的区别 Collection是在java.util包下面的接口,是集合框架层次的父接口.常用的继承该接口的有list和set. ...

  4. Java集合类的详解与应用

    Java集合类的详解与应用 集合简介: 1.定义:可以同时存储不同类型的数据 他的存储空间会随着数据的增大而增大 2.缺点:只能存储引用数据类型 3.优点:更加合理的利用空间,封装了更多的方法,用起来 ...

  5. 【集合框架】JDK1.8源码分析之ArrayList详解(一)

    [集合框架]JDK1.8源码分析之ArrayList详解(一) 一. 从ArrayList字表面推测 ArrayList类的命名是由Array和List单词组合而成,Array的中文意思是数组,Lis ...

  6. 最强Java并发编程详解:知识点梳理,BAT面试题等

    本文原创更多内容可以参考: Java 全栈知识体系.如需转载请说明原处. 知识体系系统性梳理 Java 并发之基础 A. Java进阶 - Java 并发之基础:首先全局的了解并发的知识体系,同时了解 ...

  7. [转载]Java迭代器(iterator详解以及和for循环的区别)

    Java迭代器(iterator详解以及和for循环的区别) 觉得有用的话,欢迎一起讨论相互学习~[Follow] 转载自 https://blog.csdn.net/Jae_Wang/article ...

  8. ArrayList详解-源码分析

    ArrayList详解-源码分析 1. 概述 在平时的开发中,用到最多的集合应该就是ArrayList了,本篇文章将结合源代码来学习ArrayList. ArrayList是基于数组实现的集合列表 支 ...

  9. Java 字符串格式化详解

    Java 字符串格式化详解 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 文中如有纰漏,欢迎大家留言指出. 在 Java 的 String 类中,可以使用 format() 方法 ...

随机推荐

  1. [转]【Servlet】Servlet的访问过程

    创建时间:6.15 Servlet的访问过程 1. 画图描述整个访问过程: *每次访问service()方法都会创建一对新的request和response对象,都不一样 2. 访问过程2: 问题:对 ...

  2. isaster(Comet OJ - Contest #11D题+kruskal重构树+线段树+倍增)

    目录 题目链接 思路 代码 题目链接 传送门 思路 \(kruskal\)重构树\(+\)线段树\(+\)倍增 代码 #include <set> #include <map> ...

  3. Rikka with Travels(2019年杭电多校第九场07题+HDU6686+树形dp)

    目录 题目链接 题意 思路 代码 题目链接 传送门 题意 定义\(L(a,b)\)为结点\(a\)到结点\(b\)的路径上的结点数,问有种\(pair(L(a,b),L(c,d))\)取值,其中结点\ ...

  4. Maven 中 dependencyManagement 标签使用

    1.在Maven中dependencyManagement的作用其实相当于一个对所依赖jar包进行版本管理的管理器. 2.pom.xml文件中,jar的版本判断的两种途径 1:如果dependenci ...

  5. js语法中一些容易被忽略,但会造成严重后果的细节

    一.复杂数据类型-“对象”的地址引用方式,不理解清楚,会出大乱子 复习一下基础概念(老司机略过): JS的数据可以分为简单类型(数字.字符串.布尔值.null和undefined)和 复杂数据类型(对 ...

  6. java.lang.IllegalArgumentException: Invalid character found in the request target. The valid charact

    线上环境中部署的 Tomcat 项目,出现部分页面无法打开的情况,但本地环境是好的.经过排查发现,本地 Tomcat版本为 7.0.77,而线上版本为 7.0.88.报错的具体描述为java.lang ...

  7. Python进阶-I 初识函数(function)

    函数 在java中叫方法. 函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段. 函数能提高应用的模块性,和代码的重复利用率.你已经知道Python提供了许多内建函数,比如print() ...

  8. 排序算法-基数排序(Java)

    package com.rao.sort; import java.util.*; /** * @author Srao * @className RadioSort * @date 2019/12/ ...

  9. 【java异常】【redis】ERR Client sent AUTH, but no password is set

    项目中使用jedis或redisson连接redis时,如果redis没有密码,但在配置文件中写为 spring: redis: database: 0 host: 127.0.0.1 passwor ...

  10. 从一段文字中提取出uri信息

    package handle.groupby; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io ...