网站首页 > 基础教程 正文
简介
ArrayList是一个动态数组,可以通过泛型来决定数据中存放的元素类型
怎么实现的动态
之所以是动态数组,是因为在使用无参构造函数创建ArrayList之后,默认为空数组
java复制代码public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
在你每次去add一个元素的时候,通过ensureCapacityInternal方法去检查添加后元素的个数是否会超出当前数组的长度,如果超出,数组将会进行扩容,以满足添加数据的需求
java复制代码public boolean add(E e) {
// size为当前数据存放元素的数量
ensureCapacityInternal(size + 1);
// elementData为ArrayList内部存放数据的数组
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
// 判断数组是否为空,为空则将最小容量设置为默认值10
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
// 判断当前容量是否满足最小容量
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
// modCount用于记录修改次数,作用于fast-fail机制
// 如果在迭代的时候发现modCount被修改了,则会抛出异常来避免可能会出现的不确定行为
modCount++;
// 如果当前容量小于最小容量,则进行扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
// 计算新的容量,扩大为原容量的1.5倍, oldCapacity >> 1 运算表示右移一位,也就是相当于除以二并取整
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 如果新容量超过了数组的最大容量,调用hugeCapacity方法来获取一个更大的容量
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 拷贝原数组,将原数组的元素复制到新数组中
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0)
throw new OutOfMemoryError();
// 如果最小容量大于最大容量,则将容量设置为Integer.MAX_VALUE
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
从上面的源码解释可以看到,如果新建数据没有给定初始值,ArrayList是每次在进行add操作的时候去进行扩容的,扩容则会涉及到Arrays.copyOf深拷贝,会造成内存和性能的消耗,所以在生产项目中,推荐大家尽量在创建ArrayList对象的时候就指定其容量。
Fast-Fail机制
大家肯定注意到了上面提到的通过modCount实现的Fast-Fail机制
Fast-Fail是一种机制,用于检测在迭代过程中是否有其他线程对集合进行了修改。当一个线程在迭代 ArrayList 时,如果其他线程对 ArrayList 进行了结构性修改(如添加、删除元素),那么迭代器会立即抛出 ConcurrentModificationException 异常,以避免出现不确定的行为。 例子:
java复制代码public class Example1 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>(3);
list.add("qwe");
list.add("asd");
list.add("zxc");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
String next = iterator.next();
System.out.println(next);
list.remove(next);
}
}
}
输出:
qwe
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
at com.example.lepractice.collection.Example1.main(Example1.java:21)
造成快速失败的常见操作包括:
- 在迭代过程中,使用 ArrayList 的 add、remove、clear 等方法修改集合的结构。
- 多个线程同时对 ArrayList 进行修改操作。
为了避免快速失败,可以采取以下措施:
- 在迭代过程中,不要修改集合的结构。如果需要修改,可以使用迭代器的 remove 方法,对应上面的例子也就是iterator.remove()。
- 在多线程环境下,对于需要并发修改的情况,可以使用线程安全的集合类,如 CopyOnWriteArrayList。
如果使用语法糖 for (String next : list) {
System.out.println(next);
list.remove(next);
} 这样的遍历方式,其实内部也是使用的迭代器,所以会存在同样的问题
猜你喜欢
- 2024-10-12 Java中Array,List,Set,ArrayList,Linkedlist集合的区别
- 2024-10-12 Array与ArrayList的区别 arraylist和arrays
- 2024-10-12 面试官和我聊一聊 ArrayList 面试redis
- 2024-10-12 ArrayList 和 LinkedList 源码分析
- 2024-10-12 Java集合框架,我花60分钟总结,你花20分钟记忆
- 2024-10-12 ArrayList 源码浅析 arraylist源码分析
- 2024-10-12 学点算法(一)——ArrayList内部数组实现元素去重
- 2024-10-12 面试官让我聊聊 ArrayList 解决了数组的哪些问题
- 2024-10-12 秋招啦!朋友,你不会现在连泛型都不清楚吧!不会吧不会吧
- 2024-10-12 每天一道面试题之Arraylist 与 LinkedList 区别
- 最近发表
- 标签列表
-
- gitpush (61)
- pythonif (68)
- location.href (57)
- tail-f (57)
- pythonifelse (59)
- deletesql (62)
- c++模板 (62)
- css3动画 (57)
- c#event (59)
- linuxgzip (68)
- 字符串连接 (73)
- nginx配置文件详解 (61)
- html标签 (69)
- c++初始化列表 (64)
- exec命令 (59)
- canvasfilltext (58)
- mysqlinnodbmyisam区别 (63)
- arraylistadd (66)
- node教程 (59)
- console.table (62)
- c++time_t (58)
- phpcookie (58)
- mysqldatesub函数 (63)
- window10java环境变量设置 (66)
- c++虚函数和纯虚函数的区别 (66)