今天和朋友聊天,谈到java中的集合类问题,对于其中的用法和特点有些遗忘的地方,再次梳理和记录下。首先是常用的ArrayList 和Vector。(java 8),狭义(不包含Map)集合类UML 图:
联系和区别:
ArrayList 和Vector 都继承了AbstractList,而和LinkedList 同时实现了List 接口,因此具有List的一些特点,比如底层都是用数组来存储的。也因此两者有数组有关的优缺点比如根据下标进行访问,查询速度快,时间复杂度O(1),缺点是添加和删除涉及到元素的复制、移动问题,效率相对低的缺点。提供了迭代器遍历的功能
1、ArrayList是线程不安全的类;而Vector是线程安全的集合类,源码中Vector类对集合的元素操作时都加了synchronized,保证线程安全。也因此在不考虑线程安全的前提下,相比Vector,效率上ArrayList 相对高效写(大家都这么说)
2、动态扩展方面:Vector默认扩容是先判断整张因子是否大于0,(默认为0),若大于0,则将数组大小扩大为size+capacityIncrement,否则增长一倍的容量,Arraylist是增长50%的容量。
3、LinkedList 是java提供的双向链表,进行节点插入删除要高效很多,但是随即访问要比动态数组慢,不需要像上面两种调整容量,线程也是不安全的。
因此,在选用哪个集合类存储时要根据使用场景进行针对性的选择。
源码分析:
首先是ArrayList :
构造方法:
/**
* Constructs an empty list with the specified initial capacity.
*
* @param initialCapacity the initial capacity of the list
* @throws IllegalArgumentException if the specified initial capacity
* is negative
*/
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* Constructs a list containing the elements of the specified
* collection, in the order they are returned by the collection's
* iterator.
*
* @param c the collection whose elements are to be placed into this list
* @throws NullPointerException if the specified collection is null
*/
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
可以看到,调用无参构造函数,有初始化容量的话,创建一个初始化容量大小的数组,没有到话创建一个空数组,在调用add 方法时会进行真正初始化.使用默认的初始化容量10.
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
// 将“修改统计数”+1,该变量主要是用来实现fail-fast机制的
modCount++;
// 防止溢出代码:确保指定的最小容量 > 数组缓冲区当前的长度
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
其中ensureCapacityInternal()方法当第一次数组为空时,将minCapacity 设置为10,后续比较时,当添加的元素小于10个的时候不扩容,当添加第11 个时,此时ensureCapacityInternal(int minCapacity =11),比较后11- 10>0,进行扩容。
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
// 运算符 >> 是带符号右移
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
进行右移后时间上是*0.5。
Vector:
先看下构造方法:
无参构造
/**
* Constructs an empty vector so that its internal data array
* has size {@code 10} and its standard capacity increment is
* zero.
*/
public Vector() {
this(10);
}
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
public Vector(int initialCapacity, int capacityIncrement) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
this.capacityIncrement = capacityIncrement;
}
和ArrayList 类似,默认Vector 也会创建一个初始容量为10的数组,其容量增量为0;
add()方法:
可以看到,add方法上已经使用了synchronized .
public synchronized void addElement(E obj) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = obj;
}
private void ensureCapacityHelper(int minCapacity) {
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
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);
}
可以看到,Vector 在扩容的时候,先判断增量因子是否大于0 ,大于0的话在原来的基础上+capacityIncrement,否则的话就是加上oldCapacity.
关于其他方法,就不一一介绍了,最好是通过Demo+debug的方式一步一步跟踪,方得始终,就酱。