arraylist 的扩容机制_Java集合框架之ArrayList

128898c112531f713c7a1c0506e91741.png

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扩容大致思路如下:

  1. 将原容量值赋值给临时变量oldCapacity
  2. 新的容量值(newCapacity)设置为原来的容量加原来容量的一半
  3. 比较newCapacity与size+1,如果newCapacity<size+1则将size+1赋值给newCapacity
  4. 比较newCapacity与MAX_ARRAY_SIZE的值,如果newCapacity>MAXARRAY_SIZE,调用hugeCapacity,比较size+1与MAX_ARRAY_SIZE,如果size+1>MAX_ARRAY_SIZE则返回Integer.MAX_VALUE否则返回MAX_ARRAY_SIZE
  5. 调用Arrays.copyOf()方法将原数组复制到扩容后的数组(ArrayList底层为Object数组)

源码如下:

private 

ArrayList的常用方法

  • add():添加元素
  • remove():异常元素
  • size():返回列表中的元素个数
  • isEmpty():判断列表是否为空
  • contains():判断列表中是否包含某元素
  • indexOf():返回元素的索引位置
  • lastIndexOf():返回元素最后一次出现的索引位置
  • toArray():将列表转换为Object数组
  • get():获取某一索引对应则元素
  • set():设置元素
  • clear():将列表清空
  • subList():获取子列表
  • addAll():将Collection中的元素全部添加至列表

ArrayList在遍历中删除元素

  • 使用for遍历删除,例如:
public 

当使用此方式进行遍历删除时抛出异常,运行结果如下:

75974616144c6b51b84e2079f36e2b81.png

根据反编译结果可知,foreach循环在实际执行时其实使用的时Iterator,使用的核心方法时hasNext()和next()。反编译结果(使用XJad进行反编译)如图:

0501f9b714064be858c03c5103f44af2.png

由ArrayList的Iterator实现可知,next()方法执行时会先调用checkForComodification()比较modCount与expectedModCount这两个变量的值,当这两个变量的值不相等时抛出ConcurrentModificationException。

在上面的示例中,开始遍历时modCount和expectedModCount的值都是4,当执行remove()方法后,第二次获取元素时modCount变为5,此时modCount与ExpectedModCount不相等,抛出异常。

next()方法源代码如下:

public 

fastRemove()方法源代码如下:

private 

List中边遍历边删除的三种方式

  • 使用Iterator的remove()方法
  • for循环正序遍历
  • for循环倒序遍历
  1. 使用Iterator的remove()方法的实现如下:
public 

输出结果为:

fe53284d65790162c9bdad0eb8584542.png

为什么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是线程不安全的,不建议在多线程环境中使用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值