类属性与继承关系:
public class ArrayList<E> extends AbstractList<E>
implements List, RandomAccess, Cloneable, java.io.Serializable
{
/**
* 版本号
*/
private static final long serialVersionUID = 8683452581122892189L;
/**
* 默认容量(缺省情况下)
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* 空对象数组
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* 缺省空对象数组
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* 元素数组
*/
transient Object[] elementData; // non-private to simplify nested class access
/**
* 实际元素大小(默认值为0)
*/
private int size;
/**
* 最大数组容量
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
}
1.ArrayList 实现了RandomAccess, Cloneable, java.io.Serializable三个标记接口,表示它自身支持快速随机访问,克隆,序列化
2.类的属性中核心的属性为elementData,类型为Object[],用于存放实际元素,并且被标记为transient,表明其不使用Java默认的序列化机制来实例化,但是该属性是ArrayList的底层数据结构,在网络传输中一定需要将其序列化,之后使用的时候还需要反序列化,那不采用Java默认的序列化机制,那采用什么呢?直接翻到源码的最下边有两个方法:writeObject(java.io.ObjectOutputStream s)与readObject(java.io.ObjectInputStream s),发现ArrayList自己实现了序列化和反序列化的方法。
构造方法:
public ArrayList() {
// 无参构造函数,设置元素数组为空
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
public ArrayList(int initialCapacity) {
// 当初始容量大于0时
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
// 当初始容量等于0时
} else if (initialCapacity == 0) {
// 为空对象数组
this.elementData = EMPTY_ELEMENTDATA;
} else {
// 当初始容量小于0时,抛出异常
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
public ArrayList(Collection extends E> c) {
// 将参数集合c转化为数组
elementData = c.toArray();
// 当参数c为非空集合时
if ((size = elementData.length) != 0) {
// 当未成功转化为Object[]类型数组时
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// 当参数c为空集合时,设置元素数组为空
this.elementData = EMPTY_ELEMENTDATA;
}
}
add操作:
public boolean add(E e) {
// 确保对象数组elementData有足够的容量,如不足,就进行扩容
ensureCapacityInternal(size + 1);
// 将新元素e插入数组并且size+1
elementData[size++] = e;
return true;
}
public void add(int index, E element) {
// 校验index
rangeCheckForAdd(index);
// 确保对象数组elementData有足够的容量,如不足,就进行扩容
ensureCapacityInternal(size + 1);
// 将当前数组index至size-1 的元素统一 往后移动一位
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
// 将新元素插入数组
elementData[index] = element;
// size+1
size++;
}
扩容的过程及操作:
private void ensureCapacityInternal(int minCapacity) {
// 判断当前元素数组为默认提供的空数组
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
// 比较参数minCapacity与默认容量值DEFAULT_CAPACITY ,取其较大值
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
//结构性修改+1
modCount++;
// 当参数容量值minCapacity 大于当前元素数组长度时
if (minCapacity - elementData.length > 0)
//对数组进行扩容
grow(minCapacity);
}
private void grow(int minCapacity) {
// 旧容量
int oldCapacity = elementData.length;
// 新容量为旧容量的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 当前新容量小于参数指定容量时
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 当前新容量大于最大容量值时
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 拷贝扩容
elementData = Arrays.copyOf(elementData, newCapacity);
}
其他常用方法:
public E set(int index, E element) {
// 校验索引index
rangeCheck(index);
// 旧值
E oldValue = elementData(index);
// 该位置替换为新值
elementData[index] = element;
// 返回旧值
return oldValue;
}
public E get(int index) {
// 校验索引index
rangeCheck(index);
// 返回元素
return elementData(index);
}
E elementData(int index) {
//返回元素,并将Object转型为E
return (E) elementData[index];
}
public E remove(int index) {
// 校验索引index
rangeCheck(index);
// 结构性修改+1
modCount++;
// 旧值
E oldValue = elementData(index);
// 需要移动元素的个数
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
// 赋值为null,有利于进行GC
elementData[--size] = null;
// 返回旧值
return oldValue;
}
public boolean remove(Object o) {
// 当参数o为null时,移除数组中第一个null
if (o == null) {
// 遍历数组并依次判断是否为null
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
//该方法与remove(int index)类似,只是不进行对索引index校验
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
/*
* 返回第一个出现参数o的索引值
*/
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
//若不包含,返回-1
return -1;
}
public void clear() {
//结构性修改+1
modCount++;
// 遍历数组并依次赋值为null,有利于进行GC
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
1.关于ArrayList的描述:
以数组实现。节约空间,但数组有容量限制。超出限制时会增加50%容量,用System.arraycopy()复制到新的数组。因此最好能给出数组大小的预估值。默认第一次插入元素时创建大小为10的数组。
按数组下标访问元素-get(i)、set(i,e) 的性能很高,这是数组的基本优势。
如果按下标插入元素、删除元素-add(i,e)、 remove(i)、remove(e),则要用System.arraycopy()来复制移动部分受影响的元素,性能就变差了。
越是前面的元素,修改时要移动的元素越多。直接在数组末尾加入元素-常用的add(e),删除最后一个元素则无影响。
2.什么情况下你会使用ArrayList?什么时候你会选择LinkedList?
我们知道ArrayList和LinkedList的数据结构是不同的,ArrayList是以连续的数组进行存储的,所以它的get是常数级别的,LinkedList是双向链表存储的。他的查询最坏情况是n。以为ArrayList是数组存储的,所以当你查找某一指定索引的数据时,它每次删除和指定索引添加都要移动数组的位置,其内部的实现方式是数组复杂用到System.arraycope,是比较影响性能的,而双向链表删除和插入只要找到相应的结点位置,关联下指针,所以性能会更好。
3.如何复制某个ArrayList到另一个ArrayList中去?写出你的代码?
下面就是把某个ArrayList复制到另一个ArrayList中去的几种技术:
使用clone()方法,比如ArrayList newArray = oldArray.clone()
使用ArrayList构造方法,比如:ArrayList myObject = new ArrayList(myTempObject)
其他
4.在索引中ArrayList的增加或者删除某个对象的运行过程?效率很低吗?
这个问题同2,因为添加删除某个索引的数据时,需要整体移动数组,所以效率比较低。
5.ArrayList和Vector的关系?
Vector与ArrayList基本是一致的,也是基于数组实现的,都有扩容机制,都允许元素为null。
但是不同点也是很明显的,这也是面试容易被问到的地方:
(1)Vector是线程安全的,会在可能出现线程安全的方法前面加上synchronized关键字。
(2)在构造方法上面, Vector比 ArrayList多了一个方法,在这个方法中可以新传入了一个叫capacityIncrement的参数,用于自定义扩容。
public Vector(int initialCapacity, int capacityIncrement) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);
this.elementData = new Object[initialCapacity];
this.capacityIncrement = capacityIncrement;
}
(3)两者的扩容方案不同。
//Vector的扩容方法
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
在ArrayList中,扩容的时候一般都是增加0.5倍左右,而在 Vector中,如果在构造方法中指定了capacityIncrement的值,就会在原来容量的基础上线性增加capacityIncrement个,如果没有指定这个参数,则默认增加一倍容量(如果新容量还不够,则直接设置新容量为所需的容量),而后同样用Arrays.copyof()方法将元素拷贝到新的数组。
(4)Vector已经被废弃掉
参考
http://www.cnblogs.com/huaizuo/p/5379334.html
http://blog.csdn.net/seu_calvin/article/details/52742373
http://calvin1978.blogcn.com/articles/collection.html/
http://www.importnew.com/9928.html/