Java集合框架(三):ArrayList
概要:
ArrayList继承自AbstractList,并且实现了接口List,RandomAccess,Cloneable, java.io.Serializable。其内部通过一个对象数组Object[] elementData保存数据,该对象通过transient关键字修饰,访问权限是default。故此ArrayList内部数据无法序列化,并且其内容同个包内部都可以直接访问。通过私有变量size保存当前元素个数,即ArrayList大小。
此外还提供几个私有静态常量。如下:
1
2
3
4
5
6
7
8//初始化的ArrayList容量大小
private static final int DEFAULT_CAPACITY = 10;
//创建ArrayList是,默认是一个空ArrayList而不是null,这个变量是为了优化ArrayList空实例时,避免产生过多的不必要的空数组。
private static final Object[] EMPTY_ELEMENTDATA = {};
//确保无参构成函数创建的实例在添加第一个元素时,最小的容量是默认大小10。
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//
private static final long serialVersionUID = 8683452581122892189L;
ArrayList内部是通过Object数组方式保存数据,size保存当前大小。但是存在默认构造函数,当调用该函数的时候,会产生一个默认大小的空ArrayList。这样会存在很多不必要的空数组,影响性能,ArrayList内部定义了几个静态常量空数组,用来应对空数组的共享,避免产生过多的空数组,从而提高性能。
ArrayList内部定义了四个内部类:ArrayListSpliterator、Itr、Listltr、SubList
主要方法说明;
构造方法:
ArrayList提供了3个构造方法,分别是无参数的默认构造方法,接收其他Collection对象的参数方法,以及指定ArrayList大小参数的方法。
1 | public ArrayList(): |
默认构造方法,这个方法构造的ArrayList,是一个空的ArrayList,内部共享DEFAULTCAPACITY_EMPTY_ELEMENTDATA
。
</br>
1 | public ArrayList(int initialCapacity): |
这个构造器可以指定一个默认的容量参数,如果initialCapacity
大于0,则再堆里面申请对应大小的Object数组;如果为0,则和默认构造方法一样,如果是小于零,则抛出异常IllegalArgumentException
(非法参数异常)。
</br>
1 | public ArrayList(Collection<? extends E> c): |
这个构造器,可以把E子类类型的Collection对象赋值给ArrayList的方式创建一个新的ArrayList。新ArrayList大小和参数c大小一致,内存拷贝到一个新的内存空间。
</br></br>
添加元素方法:
ArrayList提供了5个添加元素的方法。
1
public boolean add(E e):
往队列尾添加元素,其内部调用另外一个方法1
private void add(E e, Object[] elementData, int s)
进行数据添加操作。
</br>
1
private void add(E e, Object[] elementData, int s):
这是一个帮助函数,其存在目的是为了减少add(E e)
字节码大小,从而可以被C1-编译器作为内联函数展开,从而循环调用add。外部参考。
</br>
1
public void add(int index, E element):
在特定的索引位置,插入特定的元素。如果存在对应的元素在该索引位置之后,则需要把这部分元素往后平移位置。
</br>
1
public boolean addAll(Collection<? extend E> c):
把c的Collection对象所有元素都添加ArrayList尾部,如果内存空间不足,则会自动扩容,再添加;操作成功,返回true;操作失败返回false,比如c长度为0。
</br>
1
public boolean addAll(int index, Collection<? extend E> c):
在指定索引index,位置插入c的元素,索引右边的元素依次往后平移。次操作要是当前容量不够,则会自动扩容。操作成功返回true。操作失败返回false, 比如c长度为0.
删除元素:
1 | public boolean retainAll(Collection<?> c) |
retainAll
是batchRemove
外壳,该函数是把c以外的其他元素都删除。
</br>
1 | public E remove(int index): |
</br>
1 | public boolean remove(Object o): |
</br>
1 | public boolean removeAll(Collection<?> c): |
</br>
1 | public boolean removeIf(Predicate<? super E> filter): |
</br>
1 | public boolean removeIf(Predicate<? super E> filter, int i , final int end): |
</br>
1 | protected void removeRange(int from Index, int toIndex): |
</br>
1 | public void clear(): |
</br>
元素访问;
元素遍历:
ArrayList内部是通过数组方式存储数据,其遍历方式可以直接通过数组下标的方式进行遍历也可以通过ArrayList提供的迭代器方式进行遍历。下面分别对两种方式进行描述:
2.4.1.1 下标方式:
1 | import java.util.ArrayList; |
以上demo代码,首先调用add方法添加3个元素,然后通过size()方法获取数组大小,在for循环内部,通过get()方法,依次访问对应索引的元素个数,逐一遍历。
迭代器方式:
1 | import java.util.ArrayList; |
以上demo代码,使用迭代器的方式进行遍历。代码中第一次遍历是通过显示定义和调用迭代器方法;第二次是隐式调用,for-each循环,编译器自动调用迭代器进行元素遍历。
ArrayList内部迭代器类:
2.4.1.2节中迭代器方式进行遍历,是通过ArrayList内部定义的迭代器类,进行实现。下面直接通过代码分析的方式进行迭代器的功能分析:
1 | public Iterator<E> iterator() { |
通过以上代码分析可以得知,迭代器内部其实就是通过索引的方式进行迭代数据,和先获取size然后通过get方法本质上不会差别太大,而且性能上还不如get方式。另外remove方法只能是next()方法返回的元素,而且只能连续调用1次。remove一个元素以后,会引起其他元素的位置平移。