ArrayList
ArrayList是Java集合框架中的一个重要的类,也是大家经常用到的类。她是如何实现的了?
根据上图我们来分析下.
- 她继承AbstractList类
- 实现List<E>接口:说明她是一个可变长度的集合
- 实现RandomAccess接口:说明她支持快速访问
- 实现Cloneable接口:说明她可被复制
- 实现Serializable接口:说明她可被序列化
- 提供了增、删、改、查等相应功能
源代码分析:
transient Object[] elementData;
通过上面的代码我们可以看出ArrayList的存储方式其实是按照数组方式存储、为类内部访问权限且是Object类型,表明我们常用的数据类型基本都可存储于ArrayList中。
private static final int DEFAULT_CAPACITY = 10;
ArrayList集合默认长度为10。
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
ArrayList初始化数组定义,为应对不用的构造函数(如下) ArrayList提供了3种构造函数
- public ArrayList(int initialCapacity):根据参数(长度)初始化指定长度的Object数组
- 当长度大于0则创建指定长度Object数组
- 长度为0则创建一个不设置长度的Object对象EMPTY_ELEMENTDATA
- 如果长度小于0则抛出参数不合法异常IllegalArgumentException
- public ArrayList():构建一个不设置长度(长度根据集合大小变化)Object数组DEFAULTCAPACITY_EMPTY_ELEMENTDATA
- public ArrayList(Collection<? extends E> c):根据一个集合创建Object数组
- 将集合c转为数组
- 将ArrayList的长度修改为集合长度
- 如果数组类型不是Object[].class则将数组类型转换为Object[].class类型
- ArrayList长度修改失败则初始化一个可变长度的空集合EMPTY_ELEMENTDATA
- public void trimToSize():重新根据集合长度构造/复制数组使集合长度与数组长度一致。
- modCount是父类AbstractList继承过来用于记录集合修改次数。
- 重置集合长度,当集合的长度size小于存储数组(elementData)的长度(length)则重新根据集合长度构造/复制数组使集合长度与数组长度一致。
- 博主看到这个方案还是纠结了比较久。什么时候会用到该方法?当使用该方法的时候是否意味着我们的集合将面临丢失数据的情况(答案在下方)。
- public void ensureCapacity(int minCapacity):该方法为集合/数组扩容,数组对象不是DEFAULTCAPACITY_EMPTY_ELEMENTDATA则原有长度为0否则为默认值10,如果当前数组长度小于准备扩容的长度则调用ensureExplicitCapacity实现扩容。
- private void ensureCapacityInternal(int minCapacity):如果当前数组对象为DEFAULTCAPACITY_EMPTY_ELEMENTDATA则比较准备修改的长度是否小于默认值大小,调用ensureExplicitCapacity执行修改
- private void ensureExplicitCapacity(int minCapacity):集合修改次数自增一次,调用grow实现扩容
- private void grow(int minCapacity):扩容操作实际实现。先将数组长度修改为原长度的1.5倍(oldCapacity >> 1 将oldCapacity2进制移动1位相当于除以2)如果还是不够则修改为为参数指定长度,如果参数指定长度大于默认最大值MAX_ARRAY_SIZE,小于则使用Arrays.copyOf复制,不足的参数使用null不足,反之则调用hugeCapacity检查长度是否溢出。
- private static int hugeCapacity(int minCapacity):检查重置长度是否合规,小于0抛出异常,移除则取默认最大值。
- public int size():获取集合长度
- public boolean isEmpty():验证集合是否为空
- public boolean contains(Object o):集合是否包含指定的元素
- public int indexOf(Object o):对象在集合中第一次出现的索引,不存在返回-1
- public int lastIndexOf(Object o):对象在集合中最后一次出现的索引,不存在返回-1
- public Object clone():复制一个具有相同属性的集合对象,注:返回值为全新对象,新旧对象的任何操作互不影响,复制出的对象的操作次数设置为0
- public Object[] toArray:集合转为数组Object[]
- public <T> T[] toArray(T[] a) 返回指定类型的数组T[]
- E elementData(int index):返回指定位置的元素(私有)
- public E get(int index):返回指定索引位置的元素(公有)
- public E set(int index, E element) :重新赋值指定位置元素
- public boolean add(E e) :添加元素到集合结尾
- public void add(int index, E element) :向指定索引位置添加元素,后续元素索引+1
- public E remove(int index):移除指定索引位置的元素,后续元素索引-1
- public boolean remove(Object o):移除元素对象。注:如存在重复元素则只会移除第一个
- private void fastRemove(int index):移除集合指定索引位置的元素(私有)
- public void clear():将集合中所有索引的对象设置为null并把size设置为0。题外话:调用这个方法后在释放集合对象之前,集合都将占用内存空间,所以使用后最好跟着调用trimToSize()方法将集合多余内存释放(个人意见,大家有什么好的看法,请纠正)
- public boolean addAll(Collection<? extends E> c):在集合后面追加集合
- public boolean addAll(int index, Collection<? extends E> c):向指定索引位置添加集合列表
- protected void removeRange(int fromIndex, int toIndex):移除指定索引范围内的所有元素
- private void rangeCheck(int index) :验证指定位置元素是否存在,不存在则抛出异常(注:如果索引<0不会抛出异常)
- private void rangeCheckForAdd(int index):验证指定位置元素是否存在,不存在则抛出异常(包括<0,别被名字误导,不光是验证是否可插入)
- private String outOfBoundsMsg(int index) :拼接消息字符串
- public boolean removeAll(Collection<?> c):根据集合移除集合中存在的所有元素
- public boolean retainAll(Collection<?> c):根据集合移除集合中不存在的所有元素(removeAll反义)
- private boolean batchRemove(Collection<?> c, boolean complement):根据参数执行移除
- private void writeObject(java.io.ObjectOutputStream s):将集合写入ObjectOutputStream流中
- private void readObject(java.io.ObjectInputStream s):将流中的对象读取出来(跟writeObject对应)这两个方法似乎有些鸡肋,均为私有方法外部无法访问且内部并未调用。
- public ListIterator<E> listIterator(int index):返回指定索引位置开始的迭代器
- public ListIterator<E> listIterator();返回集合所有元素的迭代器
- public Iterator<E> iterator():使用内部类Itr返回迭代器
- public List<E> subList(int fromIndex, int toIndex):返回fromIndex到toIndex子集合
- static void subListRangeCheck(int fromIndex, int toIndex, int size):验证获取子集合的索引起止位置是否存在,不存在抛出异常
- public void forEach(Consumer<? super E> action):定义Lambda表达式遍历方法forEach(Java8新特性)ArrayList间接实现了Iterable接口,该方法是Iterable的 forEach实现/重写。Iterable的forEach代码如下
/**
* 对每个{@code Iterable}元素执行特定的操作直到所有的元素被处理或者抛出一个异常。
* 除非实现类另有定义,否则操作将被按照顺序迭代器的顺序执行(如果迭代器被指定)。
* 操作抛出的异常将传递给其调用者。
*
* @implSpec
* <p>默认实现类似下边这样::
* <pre>{@code
* for (T t : this)
* action.accept(t);
* }</pre>
*
* @param action 每个元素将要被执行的操作
* @throws NullPointerException 如果指定的操作action是空的
* @since 1.8
*/
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
- public Spliterator<E> spliterator():并行遍历器实现(Java8新特性)重写List、Collection的spliterator方法实现
- 想了解Spliterator的同学请移步:灵小帝-《JDK8源码之Spliterator并行遍历迭代器》
- public boolean removeIf(Predicate<? super E> filter):移除满足条件的所有元素(Java8新特性)Collection的removeIf实现
- 想了解removeIf的同学请移步:HelloMrZheng-《Java8 新特性之集合: removeIf(Predicate<? super E> filter)》
- public void replaceAll(UnaryOperator<E> operator):对每个元素执行operator指定的操作,并用操作结果来替换原来的元素。UnaryOperator是一个函数接口使用方法见下面代码
// 使用匿名内部类实现
ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));
list.replaceAll(new UnaryOperator<String>(){
@Override
public String apply(String str){
if(str.length()>3)
return str.toUpperCase();
return str;
}
});
- public void sort(Comparator<? super E> c):集合排序,使用了Comparator比较器来比较,实现里面的compare方法,compareTo比较之后,返回的参数是int类型,0则表示相等,1表示大于,-1负数则表示小于。