ArrayList介绍
ArrayList是一个数组列表。与Java数组相比,ArrayList相当于一个动态数组。它继承于AbstractList,实现了List, RandomAccess, Cloneable, Serializable接口。
ArrayList中的成员变量
- 默认数组容量
private
- 用于空实例的共享空数组实例
private
- 共享的空数组实例,用于默认大小的空实例
private
- 元素数据
transient
- ArrayList元素数量
private
ArrayList中的构造方法
ArrayList中共有3个构造方法
- 空参数构造方法
使用空参数构造方法可创建一个Object类型的数组
源码如下:
private
- int类型为参数可指定数组容量的构造方法
使用int类型为参数的构造方法可以实例化一个指定长度的Object类型的数组
源码如下:
public
- Collection类型为参数的构造方法
使用Coolection类型为参数的构造方法可以将一个Collection类型的对象转换为ArrayList,比如:new ArrayList<>(new HashSet<>());
源码如下:
public
ArrayList的扩容机制
说到扩容机制就得先看看ArrayList中添加元素的方法。ArrayList中共有两个添加元素的方法。
- 在末尾追加元素的添加方法
源码如下:
public
- 可指定元素索引位置的添加方法
源码如下:
public
当添加元素时,add()方法会检查是否需要扩容,如果需要扩容则调用grow()方法进行扩容。
检查是否需要扩容的大致思路如下:
调用calculateCapacity()方法判断目标对象是否为空列表(DEFAULTCAPACITY_EMPTY_ELEMENTDATA),如果是空列表则返回默认表长度与size+1的最大值。如果不是空列表则返回size+1,然后调用ensureExplicitCapacity()方法,比较calculateCapacity()方法的返回值与ArrayList的元素个数,如果返回值大于ArrayList的表长时对ArrayList进行扩容。
源码如下:
private
ArrayList扩容大致思路如下:
- 将原容量值赋值给临时变量oldCapacity
- 新的容量值(newCapacity)设置为原来的容量加原来容量的一半
- 比较newCapacity与size+1,如果newCapacity<size+1则将size+1赋值给newCapacity
- 比较newCapacity与MAX_ARRAY_SIZE的值,如果newCapacity>MAXARRAY_SIZE,调用hugeCapacity,比较size+1与MAX_ARRAY_SIZE,如果size+1>MAX_ARRAY_SIZE则返回Integer.MAX_VALUE否则返回MAX_ARRAY_SIZE
- 调用Arrays.copyOf()方法将原数组复制到扩容后的数组(ArrayList底层为Object数组)
源码如下:
private
ArrayList的常用方法
- add():添加元素
- remove():异常元素
- size():返回列表中的元素个数
- isEmpty():判断列表是否为空
- contains():判断列表中是否包含某元素
- indexOf():返回元素的索引位置
- lastIndexOf():返回元素最后一次出现的索引位置
- toArray():将列表转换为Object数组
- get():获取某一索引对应则元素
- set():设置元素
- clear():将列表清空
- subList():获取子列表
- addAll():将Collection中的元素全部添加至列表
ArrayList在遍历中删除元素
- 使用for遍历删除,例如:
public
当使用此方式进行遍历删除时抛出异常,运行结果如下:
根据反编译结果可知,foreach循环在实际执行时其实使用的时Iterator,使用的核心方法时hasNext()和next()。反编译结果(使用XJad进行反编译)如图:
由ArrayList的Iterator实现可知,next()方法执行时会先调用checkForComodification()比较modCount与expectedModCount这两个变量的值,当这两个变量的值不相等时抛出ConcurrentModificationException。
在上面的示例中,开始遍历时modCount和expectedModCount的值都是4,当执行remove()方法后,第二次获取元素时modCount变为5,此时modCount与ExpectedModCount不相等,抛出异常。
next()方法源代码如下:
public
fastRemove()方法源代码如下:
private
List中边遍历边删除的三种方式
- 使用Iterator的remove()方法
- for循环正序遍历
- for循环倒序遍历
- 使用Iterator的remove()方法的实现如下:
public
输出结果为:
为什么Iterator的remove()方法可以成功呢,是因为Iterator的remove()方法执行时将modCount的值赋值给了expectedModCount使得modCount与expectedModCount的值相等
源代码如下:
public
2. for循环正序遍历
实现代码如下:
public
3. for循环倒序遍历
代码实现如下:
public
三种方式中,个人推荐使用Iterator。
Java8中的Stream()流操作
- stream():为集合创建串行流
- parallelStream():为集合创建并行流
- filter():筛选
- map():将对应的元素按照给定的方法进行转换
- sorted():排序
- limit():限制返回个数
- reduce():聚合
- anyMatch():Stream 中任意一个元素符合传入的 predicate,返回 true
- allMatch():Stream 中全部元素符合传入的 predicate,返回 true
- noneMatch():Stream 中没有一个元素符合传入的 predicate,返回 true
ArrayList的使用场景
ArrayList是一个动态数组,也是我们最常用的集合。由于ArrayList底层使用数组实现,所以具有查询快的特点,并且ArrayList实现了RandmoAccess接口,所以ArrayList适用于查多写少的场景。同时ArrayList是线程不安全的,不建议在多线程环境中使用。