1 首先讲讲Vector
在Java2之前,要存储多个数据,是存储在Vector类中的。
通过读源代码发现Vector类的底层其实是一个Object数组,另外重要的是Vector类中的方法是同步的(synchronized)。
protected Object[] elementData;
public Vector(int initialCapacity, int capacityIncrement) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
this.capacityIncrement = capacityIncrement;
}
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
存储原理:
1.表面上把数据存储到Vector中,其实底层依然是把数据存储到Object数组中的。
2.我们发现该数组的元素类型是Object类型,意味着其中能存储任意类型的对象。
注:集合中只能存储对象,不能存储基本数据类型的值
在Java5之前,必须对基本数据类型手动装箱。
如:v.addElement(Integer.valusOf(123));
从Java5开始,支持自动装箱操作
如:v.addElement(123);
其底层依然是手动装箱。
3.集合类中存储的对象,都存储对象的引用,而不是对象本身。
呃,Vector里的方法就不提了,那是API要讲的东西。
2 下面说说ArrayList
ArrayList类是Java集合框架出现之后用来取代Vector类的(since 1.2)
二者底层原理都是基于数组的算法,一模一样。
3 区别
3.1 容量上的区别
3.1.1 Vector
初始化
Vector初始化的时候,会默认开一个大小为10的空间。
/**
* 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);
}
扩容
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);
}
capacityIncrement
参数可以在初始化时指定,未指定时默认是0,如果未指定capacityIncrement
,则以2 * oldCapacity
扩容。
3.1.2 ArrayList
初始化
在Java7之前,即使使用new ArrayList创建对象,一个元素都不存储,但是在堆空间依然初始化了容量为10的Object数组(size还是0)。
public ArrayList() {
this(10);
}
而Java7之后的代码:
ArrayList构造一个默认初始容量为10的空列表。
1.
elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; size = 0;
2.当向数组中添加第一个元素时,通过add(E e)
方法中调用的ensureCapacityInternal(size + 1)
方法,即ensureCapacityInternal(1)
3.在ensureCapacityInternal(int minCapacity)
方法中,可得的minCapacity=DEFAULT_CAPACITY=10
,然后再调用ensureExplicitCapacity(minCapacity)
方法,即ensureExplicitCapacity(10)
4.在ensureExplicitCapacity(minCapacity)
方法中调用grow(minCapacity)
方法,即grow(10)
,此处为真正具体的数组扩容的算法,在此方法中,通过elementData = Arrays.copyOf(elementData, 10)
具体实现了elementData
数组初始容量为10的构造。
摘自:http://blog.csdn.net/jdsjlzx/article/details/52675726
简单讲就是:
Java7开始,new ArrayList()
之后,底层创建了一个空数组,然后在第一次调用add
方法的时候,才重新去初始化数组,容量设置为10.
扩容
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);
}
newCapacity = oldCapacity + (oldCapacity >> 1);
等价于newCapacity = oldCapacity + (oldCapacity / 2);
也就是1.5 * oldCapacity
扩容
3.2 并发上的区别
Vector:所有的方法都使用了synchronized修饰符。线程安全但是性能较低。适用于多线程环境。
ArrayList:所有的方法都没有使用synchronized修饰符。线程不安全但是性能较高。
但,即使以后在多线程环境下,我们也不使用Vector类。
推荐使用下面的:
ArrayList list = Collections.synchronizedList( new ArrayList() );
源代码我就不放了,在同步的实现上和Vector的区别就是:
Vector是使用同步方法实现,synchronizedList使用的是同步代码块实现。
分析:
1.我们都知道加同步锁会一定程度上影响性能,而加锁的范围与性能成反比。
2.同步代码块比同步方法加锁更加灵活(范围可自定义)
3.同步代码块可以选择对哪个对象加锁,而同步方法只能给this对象加锁。
所以说二者比较起来,synchronizedList更加灵活,因为可以指定加锁的对象。
但是需要注意一下:
synchronizedList中并没有 都 使用synchronized代码块。
所以在遍历的时候,要手动进行同步处理,另外其自身具备的方法可以同步,其自身不具备的方法,需要手动同步。例如:
@NotThreadSafe
class BadListHelper <E> {
public List<E> list = Collections.synchronizedList(new ArrayList<E>());
public synchronized boolean putIfAbsent(E x) {
boolean absent = !list.contains(x);
if (absent)
list.add(x);
return absent;
}
}
@ThreadSafe
class GoodListHelper <E> {
public List<E> list = Collections.synchronizedList(new ArrayList<E>());
public boolean putIfAbsent(E x) {
synchronized (list) { //获得list锁对象,其他线程将不能获得list锁来来改变list对象。
boolean absent = !list.contains(x);
if (absent)
list.add(x);
return absent;
}
}
}
摘自:http://blog.csdn.net/baidu_37464759/article/details/77683588
上面的代码A线程执行完boolean absent = !list.contains(x);
后,B线程获得list对象的锁,如果对list的值进行修改,接着A线程再向后执行,就会造成java.util.ConcurrentModificationException
异常。
上面的代码是对this
加锁,下面的代码是对list
加锁,要注意同步锁对象的选择是否正确。