目录
1.创建不指定初始化大小的构造器ArrayList对象底层的创建过程:
ArrayList 类的架构图
ArrayList对外提供的成员参数
private static final int DEFAULT_CAPACITY = 10; //如果没有指定数组的初始化容量,默认是10个
private static final Object[] EMPTY_ELEMENTDATA = {}; //用来初始化保存元素的数组(下面用不默认表示)
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; //用来初始化保存元素的数组(下面用默认表示)
transient Object[] elementData; //定义保存元素的数组
private int size; //ArrayList的大小(它包含的元素数)
ArrayList对外提供了三个构造参数:
public ArrayList(int initialCapacity) //指定初始化数组的大小
public ArrayList() //不指定初始化的大小
public ArrayList(Collection<? extends E> c) //指定一个集合类型
下面是对ArrayList源码的调试
public class TestArrayList {
public static void main(String[] args) {
//创建一个ArrayList对象
List<String> list = new ArrayList<>();
//添加一个元素
list.add("0");
//将元素添加到指定的索引
list.add(1,"1");
//获取一个元素
String s = list.get(0);
//删除一个元素 指定索引
list.remove(1);
//删除指定的元素
list.remove("2");
}
}
1.创建不指定初始化大小的构造器ArrayList对象底层的创建过程:
List<String> list = new ArrayList<>();
调用不指定初始化容量构造器的时候,初始化保存元素对象数组是用 默认 的数组。
2.向集合中插入一个元素:
list.add("0");
2.1调用到add方法,在add方法中调用 ensureCapacityInternal(size + 1) (容量内部计数方法)size+ 1是为了判断当前数组的长度是否可以存储这个元素,如果不可以会调用grow(minCapacity)方法进行容量的扩增,如果可以直接将插入的元素保存到当前已经存放元素的下一个位置。下面这个地方就是判断下一个存储的数据,是否>数组设置的容量,如果>调用grow方法进行扩容。
下面这个方法就是ArrayList进行扩容的方法。
包括add("index","2")也是同样的逻辑,只是在复制当前数组的时候把指定的index位置空出来,留给添加指定的元素。
3.获取一个元素
4.删除一个元素 指定索引
删除一个元素就是将指定删除的位置全部向前面移动一位。并将最后一个索引设置为null。
5.删除指定的元素
删除指定的索引会进行遍历整个集合。当找到这个元素的索引后,和删除指定的索引一样。
6.迭代器
迭代器Itr类是ArrayList中的私有类保证了封装性,只能通过一个公共方法去获取Itr的实例,进行调用。
总结
上面我们写了这么一大篇,是时候该来总结总结一下了
1.查询高效、但增删低效,增加元素如果导致扩容,会将所有的元素进行复制,删除元素一定会复制。增加和删除操作会导致元素复制,因此,增删都相对低效。ArrayList底层是一个数组,天然自带索引,查询操作相当于在操作指定的索引,所以查询操作是高效的,且增加操作只有在列表加载更多时才会用到 ,而且是在列表尾部插入,所以也不需要移动数据的操作。而删操作则更低频。 故选用ArrayList作为保存数据的结构
2.线程不安全,api操作不是原子性操作,当有多个线程同时执行后产生竞态条件。所以不是线程安全的操作。
扩展
1.transient 关键字有什么用?(transient关键字和序列化有直接的关系)
在ArrayList中的elementData这个数组的长度是变长的,java在扩容的时候,有一个扩容因子,也就是说这个数组的长度是大于等于ArrayList的长度的,我们不希望在序列化的时候将其中的空元素也序列化到磁盘中去,所以需要手动的序列化数组对象,所以使用了transient来禁止自动序列化这个数组,但是会序列化元素中的数据,而不是数组。
2.什么是序列化?
序列化就是可以很方便的保存内存中java对象的状态,同时也为了方便传输。
序列化时需要注意事项
序列化时最好是定义序列化版本id 即 public static final Long seriaVersionUID = 1L (默认) 或者 xxxxx L(自定义64位行)
因为反序列化会判断序列化中的id和类中的id是否一样,如果不定义虽然会自动生成,但如果后面改了东西列,所以还是自觉点定义一个id,省去好多麻烦
同时记住静态变量不会被序列化的,它可不在堆内存中,序列化只会序列化堆内存
3.ArrayList实现Cloneable接口的意义?
实现这个方法,我觉得就是为了调用clone方法的时候不报异常,实际上clone方法是一个浅度复制,只保存了元素的引用,没有保存元素的本身。