Java 集合框架之----ArrayList
ArrayList
-
ArrayList<Integer> list=new ArrayList<Integer> ();
用无参构造法创建对象后,这个对象不是null。而是一个有效存在的实例,只不过元素数组为空。
ArrayList里面可以存放null
元素。 -
Integer.MAX_VALUE
是 Java 中int
类型的最大值,等于2^31 - 1
(即 2147483647)。
MAX_ARRAY_SIZE
是ArrayList
中定义的常量,值为Integer.MAX_VALUE - 8
- 为什么需要区分:
这个设计与 Java 虚拟机的内存分配机制有关:- 数组在内存中存储时,除了元素本身,还需要额外的空间存储数组的元数据(如长度等)
- 对于某些虚拟机实现,如果数组容量达到
Integer.MAX_VALUE
,可能没有足够空间存储元数据,导致内存分配失败 MAX_ARRAY_SIZE
预留了 8 个字节的空间,避免这种情况
- 三目运算符的作用:
return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
- 当所需容量超过
MAX_ARRAY_SIZE
时,尝试使用Integer.MAX_VALUE
(尽最大可能分配) - 否则使用
MAX_ARRAY_SIZE
(安全的最大容量)
- 当所需容量超过
这个逻辑既保证了在大多数情况下的内存分配安全性,又在极端情况下尽可能满足大容量需求,是一种平衡设计。如果直接使用 Integer.MAX_VALUE
作为统一上限,在某些虚拟机环境下可能导致更多的内存分配失败。
ensureCapacityInternal(int minCapacity)
是 Java 中ArrayList
类的核心方法之一,它的主要作用是确保内部存储数组(elementData
)有足够的容量来容纳至少minCapacity
个元素,是实现动态扩容机制的关键步骤。
具体来说,这个方法的工作流程和作用如下:
-
特殊空数组的处理
首先检查当前内部数组elementData
是否是DEFAULTCAPACITY_EMPTY_ELEMENTDATA
(即通过无参构造函数创建的初始空数组)。
如果是,则将minCapacity
修正为「默认容量(通常是 10)」和「传入的minCapacity
」中的较大值。
这一步的目的是:对于无参构造的ArrayList
,第一次添加元素时会自动应用默认容量(10),避免初始容量过小导致频繁扩容。 -
触发显式扩容检查
调用ensureExplicitCapacity(minCapacity)
方法,进一步判断是否需要扩容:- 如果
minCapacity
大于当前elementData
的长度(即现有容量不足),则触发grow()
方法进行实际扩容。 - 同时会更新
modCount
(记录结构性修改次数),用于支持快速失败(fail-fast)机制。
- 如果
简单说,这个方法是 ArrayList
扩容的「前置处理器」:它先根据集合的初始化方式(无参/有参构造)调整最小需求容量,再决定是否需要执行实际的扩容操作,最终保证集合有足够空间存储元素。
Collection<? extends E> c
是 Java 泛型中的一种通配通配符(Upper Bounded Wildcard)语法,表示一个元素类型是E
或E
的子类的集合。
具体含义拆解:
Collection
:表示这是一个集合接口(如List
、Set
等都实现了该接口)<? extends E>
:泛型通配符,表示集合中的元素类型必须是E
类型或者E
的子类(例如E
是Number
,则可以接受Integer
、Double
等类型的集合)c
:参数名称,代表传入的集合对象
这个泛型约束的作用:
- 保证类型安全:确保添加到当前
ArrayList
中的元素类型与集合的泛型类型E
兼容 - 提高灵活性:允许传入任何
E
类型子类的集合,比如ArrayList<Number>
可以接收ArrayList<Integer>
作为参数 - 限定只读性:由于使用了
? extends E
,在方法内部不能向集合c
中添加元素(除了null
),只能读取其中的元素
举例说明:
如果有 ArrayList<Number> list
,那么:
- 可以传入
ArrayList<Integer>
作为参数(因为Integer
是Number
的子类) - 可以传入
ArrayList<Double>
作为参数(因为Double
是Number
的子类) - 但不能传入
ArrayList<String>
作为参数(类型不兼容)
这种泛型写法既保证了类型安全,又为方法调用提供了灵活性,是 Java 集合框架中常见的设计模式。
- remove
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; // clear to let GC do its work}
用快拷贝的方式将index+1后面的元素拷贝到index中,再把原先最末尾的指针置为null,便于垃圾回收器回收。
Itr
内部类是ArrayList
实现的迭代器(Iterator
),主要作用是提供对ArrayList
元素的遍历功能,同时保证遍历过程中的线程安全(通过快速失败机制)。
具体来说,Itr
内部类的核心功能和设计目的如下:
-
实现迭代器接口(
Iterator
)
Itr
实现了Iterator<E>
接口,必须重写三个核心方法:hasNext()
:判断是否还有下一个元素(通过cursor != size
实现,cursor
是下一个要返回的元素索引)next()
:返回下一个元素,并移动游标(cursor
自增)remove()
:删除当前迭代到的元素(通过调用ArrayList
的remove
方法实现)
-
支持集合的安全遍历
Itr
通过以下机制保证遍历的安全性:- 游标跟踪:用
cursor
记录下一个要访问的元素位置,lastRet
记录上一个访问的元素位置,确保遍历顺序正确。 - 快速失败(fail-fast):
迭代器初始化时会记录当前ArrayList
的modCount
(赋值给expectedModCount
),每次调用next()
、remove()
等方法时,都会通过checkForComodification()
检查modCount
是否与expectedModCount
一致。
如果不一致(表示集合在迭代过程中被结构性修改,如添加/删除元素),会立即抛出ConcurrentModificationException
,避免遍历一个已被修改的、可能不一致的集合。
- 游标跟踪:用
-
优化遍历性能
Itr
被注释为「An optimized version of AbstractList.Itr
」,相比父类的迭代器实现,它直接访问ArrayList
的内部数组elementData
,减少了中间调用开销,提升了遍历效率。 -
支持函数式遍历
forEachRemaining(Consumer<? super E> consumer)
方法允许通过 Lambda 表达式遍历剩余元素,例如:
list.iterator().forEachRemaining(element -> System.out.println(element));