一、简介
ArrayList位于java.util包下,它继承于AbstractList,实现了List, RandomAccess, Cloneable, java.io.Serializable这些接口。基于数组实现,是一个动态的容器,与数组的大小一旦确定不能修改不同的是,ArrayList的容量大小可以动态的变化。
ArrayList 实现了RandmoAccess接口,即提供了随机访问功能。RandmoAccess是java中用来被List实现,为List提供快速访问功能的。在ArrayList中,我们即可以通过元素的序号快速获取元素对象;这就是快速随机访问;遍历ArrayList时,使用随机访问(即,通过索引序号访问)效率最高,而使用迭代器的效率最低!
ArrayList 实现了Cloneable接口,即覆盖了函数clone(),能被克隆。ArrayList 实现java.io.Serializable接口,这意味着ArrayList支持序列化,能通过序列化去传输。
2.具体继承关系及直接子类
- java.lang.Object
- java.util.AbstractCollection<E>
- java.util.AbstractList<E>
- java.util.ArrayList<E>
- java.util.AbstractList<E>
- java.util.AbstractCollection<E>
- All Implemented Interfaces:
Serializable , Cloneable , Iterable <E>, Collection <E>, List <E>, RandomAccess
已知直接子类:
AttributeList , RoleList , RoleUnresolvedList
3、根据源码 简写其原理
由于是自己写的,可能跟源码中有些差异,但实现原理相同,易于理解,这里主要写一个ArrayList中最重要的几个方法,如
add(),get(int index),remove(int index)方法;
public class MyArrayList { // 定义动态数组,变量修饰符暂用private, // 源码用transient来修饰, // 用transient关键字标记的成员变 // 量不参与序列化过程。 private Object [] elementData; // 容器中所包含元素的个数 private int size; /** * 无参构造器,默认创建容量为10的动态数组, * 为了方便,这里不单独定义 DEFAULT_CAPACITY 了 */ public MyArrayList(){ this(10); }; /** * 有参构造器: * 创建arrayList对象的时候,可以指定初始化容器的大小 * @param initialCapacity */ public MyArrayList(int initialCapacity){ if(initialCapacity<0) { try { throw new Exception(); } catch (Exception e) { e.printStackTrace(); } } elementData = new Object[initialCapacity]; }; /** * size()方法,返回容器中元素个数 * @return */ public int size() { return size; }; /** * add()方法,向容器中添加一个元素 * 实现思路: * 1、需要判断当前容量的容器是否已经装满元素, * 若已经装满元素,则需要扩容 * (实际上就是创建一个容量为原来两倍的新数组, * 然后把旧数组中的元素再拷贝到新数组中, * 源码中是这样计算新数组的容量的: * int newCapacity = oldCapacity + (oldCapacity >> 1); * 此处运用位运算,提高运算速度 * 为了方便,我直接用原来的容量乘以2计算的。 * ) * 否则,则直接将此元素添加到容器后则 * 2、容器中元素的个数增加1 * @param obj */ public void add(Object obj) { if(size>=elementData.length) {//需要扩容 // 此处创建 Object[] newELementData = new Object[size*2]; // public static void arraycopy(Object src, int srcPos, // Object dest, int destPos, int length) // 代码解释: // Object src : 原数组 // int srcPos : 从元数据的起始位置开始 // Object dest : 目标数组 // int destPos : 目标数组的开始起始位置 // int length : 要copy的数组的长度 System.arraycopy(elementData,0,newELementData, 0,elementData.length); elementData = newELementData; } elementData[size++]=obj; }; /** * isEmpty(),判断容器是否为空 * @return */ public boolean isEmpty() { if(size==0) { return true; }; return false; }; /** * get(int index)方法 * 实现思路: * 通过下标获取当前容器中相应位置的元素, * 首先需要判断传入的下标 是否合理, * 即是否是非负数, * 并且不能大于等于容器中的元素个数 * 若果合理,则直接通过下标从数组中获取即可, * 若果不合理, * 则直接抛出异常,此处异常我没做详细处理 * @param index * @return */ public Object get(int index) { rangeCheck(index); return elementData[index]; }; // 私有方法,检查下标是否合理,只在此类中使用 private void rangeCheck(int index) { if(index<0 || index>=size) { try { throw new Exception(); } catch (Exception e) { e.printStackTrace(); } } } /** * remove(int index)方法 * 实现思路: * 1、首先判断传入参数是否合理, * 同get()方法中的判断逻辑; * 2、拷贝原数组中index之后的 size-index-1个 * 元素到index位置及之后 * 3、容器中元素的个数减1 * @param index */ public void remove (int index) { rangeCheck(index); // index位置之后的size-index-1个元素 int numMoved = size - index - 1; if(numMoved>0) { System.arraycopy(elementData,index+1,elementData,index,numMoved); }; elementData[--size] =null; } }
四,总结
说白了,arrayList就是一个数组,只是这个数组可以根据情况增加容量。
以上代码如有错误之处,请各位仁兄批评指正,下一节我会总结linkedList的源码实现过程。