文章目录
- 概述
- 初始化
- 扩容操作
- 方法解读
- forEach(Consumer<? super E> action)
之前有一个博客是对源代码的解读,有兴趣的可以直接看源代码,看一遍基本上就啥都清楚了。
概述
ArrayList是java中的一种动态数组,其底层采用数组实现,访问快插入删除慢。
以下是ArrayList的类图:
初始化
如果使用无参构造方法ArrayList()
初始链表,这个时候并不会初始化列表底层的数组,直到第一次进行插入操作的时候会对数组进行初始化。
当使用ArrayList(int initialCapacity)
初始化列表的时候,会根据指定的容量去创建底层的数组,如果输入为零,会将底层的数组赋值成一个空数组。
使用ArrayList(Collection<? extends E> c)
去创建列表的时候,如果输入的数据集合不为空,会将输入的数据集合中的元素复制到新的的列表中。
扩容操作
在数组进行插入操作的时候,会首先判断数组中的容量是否够用,如果不够的话会进行一个扩容的操作。
在进行扩容的时候首先会判断当前的数组是不是使用无参构造初始化后的首次插入,如果是的话会做一个判断:取需要的容量与默认容量的最大值(10)。
然后会再进行判断:长度增长为原来的1.5倍之后是否满足需要,如果不满足的话就取需要的最小容量。
举个栗子:原容量是10,需要的最小容量是18;进行扩容的时候增长1.5倍就不够了,就直接取需要的最小容量:18
最后还有一次判断:需要的容量是否已经超过了数组默认的最大值:如果超过了,会进入另一次判断:是否超过整型数的最大值,如果超过了,就抛出异常,然后就会取默认最大值与整型最大值可行的较小的那个。
比较巧妙的是以上所有对数据的比较操作,全部都是使用的(a-b)>0
,这样在值越界的情况下仍然可以进行比较,并且返回是正常的判断结果。
方法解读
forEach(Consumer<? super E> action)
这个方法主要是对list进行遍历操作,看名字就能猜出来是啥情况,for是遍历forEach就是遍历所有的元素。
使用这个方法可以比较优雅的对数组进行遍历,因为是直接访问底层的数组进行遍历,访问速度相对来说要快一点,也不用担心越界;但是也有一定的不足之处,首先就是无法获取当前值的索引,其次就是没有办法对数据进行修改,需要用到这两点的同学麻烦出门右转,老老实实的用for循环。
需要注意的是,这个方法引入的快速失败机制,简单来说就是在迭代期间不能有其他的线程对列表进行修改操作,其中修改列表指的是删除、添加。
额外说一点,如果链表中的数据是引用类型的话,是可以对引用类型中的数据进行修改的,但是引用的对象本身是无法改变的。
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 5; i++) {
list.add(i);
}
//可以直接写一个内部类去进行各种骚操作
//一下这个就是对数据进行遍历输出
list.forEach(
new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
System.out.println(integer);
}
}
);
//下面这两种看起来是不是就比较简单了,这个是使用了lambda表达式
//这也是直接遍历所有元素,输出到控制台
list.forEach(System.out::println);
list.forEach(n -> System.out.println(n));