一、简介
1、ArrayList继承关系
2、继承关系解析
1、顶层Iterable接口,可迭代
2、Collection继承自Iterable,并添加了size()、isEmpty()、contains等方法。
3、List继承了Collection接口,针对List的特性加入了get、set、sort、sublist等方法。
4、AbstractCollection中即有自己实现了方法体的一些函数,也有抽象方法约束子类去实现。(主要依赖于设计模式,因为具体的集合子类根据自身的特性不同,实现的方法也会不一样,如果定义成抽象方法,那么子类需要去实现)
/**
* {@inheritDoc}
*
* <p>This implementation always throws an
* {@code UnsupportedOperationException}.
*
* @throws UnsupportedOperationException {@inheritDoc}
* @throws ClassCastException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
* @throws IllegalArgumentException {@inheritDoc}
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
5、AbstractList中设计到迭代器
两个主要的函数:
public E next() {
checkForComodification();
try {
int i = cursor;//先把游标付给i
E next = get(i);//拿到游标后的第一个元素 next
lastRet = i;//将 lastRet 置为 i ,表示最新一次访问的元素位置
cursor = i + 1;//游标后移一位
return next;//返回数值
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
public void remove() {
if (lastRet < 0)//判断 lastRet 是不是 -1,如果是的话说明这个元素被一个 remove 方法调用了,不能连续调用两次 remove,会报错
throw new IllegalStateException();
checkForComodification();//如果不是 -1,紧接着判断并发操作
try {
AbstractList.this.remove(lastRet);//调用子类实现的 remove 方法删除最新访问的元素,重新更新游标
if (lastRet < cursor)
cursor--;
lastRet = -1;//上次访问的元素位置置为 -1
expectedModCount = modCount;
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
Ltr
看下来只是简单实现了 Iterator
中的 next
和 remove
方法,ListLtr
中实现了previous
方法以及set方法:
//previous 方法中,返回迭代器游标之前的一个元素,并同时设定游标值和最后一次访问元素的坐标值。当调用 previous 函数时,可能出现最后一次访问元素坐标与游标重合,其它情况下则不会出现
public E previous() {
checkForComodification();
try {
int i = cursor - 1;
E previous = get(i);
lastRet = cursor = i;
return previous;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
public int nextIndex() {
return cursor;
}
public int previousIndex() {
return cursor-1;
}
//Set 方法则是将列表最后一次访问的元素替换为 e
public void set(E e) {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
AbstractList.this.set(lastRet, e);
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
equals方法的实现:
public boolean equals(Object o) {
if (o == this)
return true;//如果是本身直接返回 true
if (!(o instanceof List))
return false;//instanve of 判断类型部署 List 的话直接 false
ListIterator<E> e1 = listIterator();
ListIterator e2 = ((List) o).listIterator();
while (e1.hasNext() && e2.hasNext()) {
E o1 = e1.next();
Object o2 = e2.next();
if (!(o1==null ? o2==null : o1.equals(o2)))
return false;//否则取双发迭代器,依次比较元素,如果其中一个为 null 又或者 二者不相等 就 false
}
return !(e1.hasNext() || e2.hasNext());
}
二、代码解析
1、构造函数
public ArrayList()//构造了一个空数组
public ArrayList(int initialCapacity)//构造了一个指定长度的数组
public ArrayList(Collection<? extends E> c)//构造了一个指定Collection的数组
2、元素添加
add(E e) 和add(int index, E element)
add(E e)是在ArrayList插入到最后,add(int index, E element)是在指定位置index插入element。如果index不是在ArrayList,就涉及到数组元素的移动。
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
逻辑主要牵扯到扩容:
/**
* 默认初始容量.
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* 用于空实例的空数组实例。如 ArrayList list = new ArrayList<>() list就等于{}.
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
public void ensureCapacity(int minCapacity) {
//判断需要扩容的数组是否为空实例(空数组)如果为不为空,则变量等于0.为空则变量等于数组默认容量 10
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
? 0
: DEFAULT_CAPACITY;
//如果需要扩容的量大于定义的变量。则进一步调用以下方法。
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
//需要扩容的量大于原数组的长度,则进一步调用方法。(其实这里就本文章对ensureCapacity()
//分析来说,我觉得这个方法完全写在上面的方法中,没必要再单独再写个方法做判断。)
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
//要分配的数组的最大大小
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
* 真正扩容的地方
*/
private void grow(int minCapacity) {
int oldCapacity = elementData.length; //原数组的长度
int newCapacity = oldCapacity + (oldCapacity >> 1); //原数组的长度+原数组的长度/2
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity; // 系统给予的扩容策略所扩的容量<用户给的扩容量,则改用用户指定扩容量
if (newCapacity - MAX_ARRAY_SIZE > 0)
// 如果需要扩容的量大于了本类中定义的最大扩容限制,则扩容到 int 类型最大长度
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);// 扩容,其实调用的的是数组的复制方法
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0)
throw new OutOfMemoryError();
// 如若需要扩容的量大于了最大限制,则扩容量改为 int 最大限制量:2147483647。否则为本类中所限制长度:2147483647-8
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
3、元素移除
remove
、removeRange
等方法的实现也差不多,例如 remove
中需要压缩列表,将最后一个位置置为 null ,让 gc
清理,因为涉及到修改的话,modCount
会 ++ ;fastRemove
为私有方法,跳过了越界检查并且不返回删除的元素值
4、元素排序
sort方法可以对ArrayList中的元素进行排序,排序使用CopyOnWrite的方法。
5、遍历
1、普通for遍历
2、迭代器iterator
public Iterator<E> iterator() {
return new Itr();
}
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
Iterator<String> it = list.iterator();
while(it.hasNext()){
String str = it.next();
System.out.print(str+" ");
}
3、迭代器的变种 forEach
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
for(String str : list){
System.out.print(str + " ");
}
4、迭代器 ListIterator
public ListIterator<E> listIterator(int index) {
if (index < 0 || index > size)
throw new IndexOutOfBoundsException("Index: "+index);
return new ListItr(index);
}
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
ListIterator<String> listIt = list.listIterator();
//向后遍历
while(listIt.hasNext()){
System.out.print(listIt.next()+" ");//a b c
}
//向后前遍历,此时由于上面进行了向后遍历,游标已经指向了最后一个元素,所以此处向前遍历能有值
while(listIt.hasPrevious()){
System.out.print(listIt.previous()+" ");//c b a
}
三、总结
1、是一个动态数组,容量可以自动扩容,不适合频繁增加元素的场景。
2、随机查询效率高,但是增删的复杂度较高。
3、非线程安全。