ArrayList 从一开始到如今,它的身影如影如行,常伴吾身 。
面试时也经常碰到询问该数据结构及原理等问题
比如说:
嘿嘿,潇潇其实用了这么久的java集合框架,真的没有做很深的研究与探讨 ,趁着这段良辰岁月,于是乎绝对好好看一下好朋友的蒙娜丽莎的面纱,哈哈 都怪自己放荡不羁爱自由吧(咳咳咳 其实都怪自己爱玩游戏0.0.)
废话说的太多 闪了舌头,上Code
public class ArrayList extends AbstractList implements List, RandomAccess, Cloneable, java.io.Serializable
AbstractList 定义了一些集合的基本行为,比如说增加,修改,删除等
Cloneable 这个类,嘻嘻就不用多说了 ,用于复制
Serializable 用于序列化,至于好处,后面有时间我在深入补充一下 嘎嘎
RandomAccess 这个挺有意思的,随机访问,说白就是根据不同算法选择不同访问数据(也就是get),实现该类 有兴趣的同学可以自己进行百度和google了解,潇潇认为并不是很重要
数据存储有序(基于数组实现)
访问数据快(基础数组下表获取数据)---> 空间复杂度 O(1)
/** * Appends the specified element to the end of this list.
* 将指定的元素追加到此列表的末尾
* 试想下再多线程,并发情况下同时添加数据,有可能造成数据覆盖,List扩容问题
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
嘻嘻,具体看上列源码add(E e)
/**
* 初始容量
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* 存储ArrayList的元素的数组缓冲区。
* 被transient拒绝序列化
* 只序列化实际存储的那些元素,而不是整个数组,从而节省空间和时间。
*/
transient Object[] elementData; // non-private to simplify nested class access
/**
* Shared empty array instance used for empty instances.
* 用于ArrayList空实例的共享空数组实例
* 包含elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA的空ArrayList
* 添加第一个元素时,
*将扩展为DEFAULT_CAPACITY
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* 共享的空数组实例,用于默认大小的空实例。 我们将此与EMPTY_ELEMENTDATA区别开来
* 以了解添加第一个元素时需要充气多少。
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
public boolean add(E e) {
ensureCapacityInternal(size + 1); //增加mod计数
elementData[size++] = e; //获取计数后下表,进行添加元素
return true;
}
/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
private void ensureCapacityInternal(int minCapacity) {
// 当第一次调用add(E e)方法的时候,判读是不是无参构造函数创建的对象,
// 如果将DEFAULT_CAPACITY即10作为ArrayList的容量,此时minCapacity = 1
// 那么minCapacity =DEFAULT_CAPACITY(10 )
// 看到上面一段代码就明白了
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++; ///增加mod计数
// overflow-conscious code 判断是否溢出 增加数必须大于当前数组长度
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
//minCapacity 所需最小容量 因为默认再数组最后一条追加
private void grow(int minCapacity) {
// overflow-conscious code
//现在数组的长度
int oldCapacity = elementData.length;
//oldCapacity + (oldCapacity >> 1) 默认新得容量扩大 * 2
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 默认扩容增加俩倍 小于 minCapacity 那么赋值给newCapacity
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//如果扩容长度大于 MAX_ARRAY_SIZE
// 默认长度(minCapacity > MAX_ARRAY_SIZE)
//?Integer.MAX_VALUE : MAX_ARRAY_SIZE;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
//调用JNI System.arraycopy 进行数组复制 扩容,不详细讲解参数 很简单 看一下就明白了
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
嘿嘿,曾经那个晚上?哪个时辰? 被我给碰到了,具体得那一年得某年某月某日可能记得不大清楚了,毕竟潇潇忘性大 总有一些风华雪月 再人生中错过 。 咳咳咳
查看ArrayList 源码发现ArrayList中创建了一个内部迭代器Itr,并实现了Iterator接口,而for-each遍历正是基于这个迭代器的hasNext()和next()方法来实现 See See See ~~~~!!!
/**
* An optimized version of AbstractList.Itr
* 基于AbstractList 优化后得itr 迭代器
*/
private class Itr implements Iterator {
//下一个要返回得元素索引
int cursor; // index of next element to return
//返回的最后一个元素的索引; 没有返回 -1
int lastRet = -1; // index of last element returned; -1 if no such
//外部的变量modCount计数 也就是ArrayList 里面modCount 变量
// 期望的修改次数
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size;
}
// ............有兴趣得自己打开源码查看
}
/**
// 删除
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
//fastRemove 跳过边界检查 ,返回返回值
fastRemove(index);
return true;
}
}
return false;
}
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
/*
* Private remove method that skips bounds checking and does not
* return the value removed.
*/
private void fastRemove(int index) {
modCount++;
// ArrayList modCount进行改变
// 内部迭代器Itr expectedModCount 没有进行更新
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
//此处进行 size
}
进行往下深入 hasNext() 判断是否还有元素 ,如果前面删除成功,remove()返回肯定为
true(如果删除若之前删除的是倒数第二个元素,此处的cursor就是最后一个索引值size(
)-1,而由于已成功删除一个元素,此处的siz也是原size()-1,两者相等,此处会返回false),
再次调用Next() checkForComodification()会抛出异常
//AbstractList 优化版本 for for each
private class ListItr extends Itr implements ListIterator {}
根据我得实战和经验 属于浅拷贝 经过线上代码 soci 毒打发现该问题,如果需要subList深复制请重新源码其它方式我并没有深入研究,或者重写该类Clone方法
深复制 拷贝数据 创建新得对象
浅复制 拷贝引用得本身 不会创建新得对象
这个也太简单了点!!!!
几种方式让ArrayList线程安全 简单列举几个 Collections.syn.....() , syncharizertion
操作加锁等等
一个执行速度快 ,线程不安全
一个线程安全执行速度慢 ,可以看源码 啊 嘎嘎, 因为太多jvm syncharizertion 修饰,syncharizertion 这个java同步关键字也有太多得道道和东西了 有时间整理一波资料好好叨叨!!!
ArrayList与LinkedList区别
ArrayList 查询速度快, 基于线性结构 线程不安全 有序
LinkedList 查询速度比ArrayList慢 ,基于双向链表结构 (实现Deque) 删除快等
private static class Node {
E item;
Node next;
Node prev;
Node(Node prev, E element, Node next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
1.7 和1.8得区别不做阐述 ,感觉区别并不是很大0.0.
由于昨日只始开始写博客,写得不对,或者有问题得地方,请大家多多指教 0,0 喵喵!!!!