本篇分析基于jdk1.8。
数组与链表的基本知识在这里不作展开,属于数据结构与算法的重点内容。
直接上几个ArrayList里的方法。
1.Add 方法
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
忽略返回值做了两件事情。
- 确保当前数组的容量
- 将需要添加的元素,也就是这里的e加到数组的末尾。size当前数组的大小,size++自然就是当前数组的后一位。
其他值得注意的是,此方法并没有做任何同步处理。并且,事实上整个Arraylist里的所有方法都没有做任何同步处理。因此这些涉及数据操作的方法都不是线程安全的。
另外elementData的定义如下:
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
*/
transient Object[] elementData; // non-private to simplify nested class access
非常简单,就是一个对象数组而已。注意,它使用了transient关键字来修饰,这意味着ArrayList不会被序列化。
2. Get方法
/**
* Returns the element at the specified position in this list.
*
* @param index index of the element to return
* @return the element at the specified position in this list
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
做了两件事情
- 检测传入的参数,即数组的下标,是否越界
- 若通过检测,则返回对应位置的元素即可,因为数组是有序排列的。
3. remove方法
/**
* Removes the first occurrence of the specified element from this list,
* if it is present. If the list does not contain the element, it is
* unchanged. More formally, removes the element with the lowest index
* <tt>i</tt> such that
* <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>
* (if such an element exists). Returns <tt>true</tt> if this list
* contained the specified element (or equivalently, if this list
* changed as a result of the call).
*
* @param o element to be removed from this list, if present
* @return <tt>true</tt> if this list contained the specified element
*/
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(index);
return true;
}
}
return false;
}
这个实现很有意思的是它判断了一下要删除的对象是否是null,为什么要做这个判断呢,因为他们判断对象是否相等的方法不一样。null直接用两个等号判断,而非null对象,则是用equals方法判断相等。
遍历数组,找到相等对象,确定下标,然后删除当前数组中该下标的元素。删除方法为fastRemove,实现如下
/*
* Private remove method that skips bounds checking and does not
* return the value removed.
*/
private void fastRemove(int index) {
modCount++;
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
}
由于是删除,所以不需要做越界检查。结构变化数modCount加一,计算需要移动的数据元素,为当前数组大小,减去删除元素位置,再减去1,为什么要减去1因为数组下标从0开始,很好理解,做个草图如下:
当前数组元素一共9个,然后假设我要删除的是第6个元素。即size=9,index=5(从0开始)。要删除的元素是6,需要移动的元素即为7,4,8一共三个。
size-index-1 = 9 - 5 -1 = 3.
计算正确,一个测试用例诞生~。
然后将五个参数传入了System类下的arraycopy方法。这个方法的作用就是根据需要移动的元素,重新构造一个没有要删除元素的新数组,也即实现了删除的功能。这里额外插一句,从clean code的角度来讲,这个方法的参数已经太多了,clean code的建议是三个最多,只能说凡事无绝对吧。或者也可以说jdk的代码也并非从各个角度来看都完美无缺。
方法arraycopy在System类看不到实现,它是一个native方法。java众多的底层方法,实现几乎都是native方法,也就是用别的语言实现的。作为一个现代的程序员,或者说作为一个流水线上的程序员,大多数人真的一辈子也许也不需要知道这个方法到底如何实现的。
那么,如果要使用线程安全的容器,应该使用哪一个呢。答案就是Vector。
Vector的add方法如下:
/**
* Appends the specified element to the end of this Vector.
*
* @param e element to be appended to this Vector
* @return {@code true} (as specified by {@link Collection#add})
* @since 1.2
*/
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
用了synchronized直接修饰这个方法。除此之外实现逻辑和ArrayList几乎一样。modCount从注释来看这个变量代表的是这个容器的结构变化次数,所谓结构变化很好理解,就是数组中元素增加了一个,减少了一个,就是结构的变化。这里把modCount放在这里做计算,是一个和ArrayList不一样的地方,ArrayList是在check的时候来做。但这仅仅是代码风格的不统一。
Vector的get方法如下:
/**
* Returns the element at the specified position in this Vector.
*
* @param index index of the element to return
* @return object at the specified index
* @throws ArrayIndexOutOfBoundsException if the index is out of range
* ({@code index < 0 || index >= size()})
* @since 1.2
*/
public synchronized E get(int index) {
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
return elementData(index);
}
也是用sychronized修饰了这个方法,然后先检查是否越界,不越界则返回,但是它没有像ArrayList一样将这个check提出一个方法,不太明白为何jdk的两个关联性非常强的类还会存在实现方法不一样,代码风格不一致的问题,虽然从功能上讲没有任何区别。与ArrayList基本一样。
Vector的remove方法如下:
/**
* Removes the first (lowest-indexed) occurrence of the argument
* from this vector. If the object is found in this vector, each
* component in the vector with an index greater or equal to the
* object's index is shifted downward to have an index one smaller
* than the value it had previously.
*
* <p>This method is identical in functionality to the
* {@link #remove(Object)} method (which is part of the
* {@link List} interface).
*
* @param obj the component to be removed
* @return {@code true} if the argument was a component of this
* vector; {@code false} otherwise.
*/
public synchronized boolean removeElement(Object obj) {
modCount++;
int i = indexOf(obj);
if (i >= 0) {
removeElementAt(i);
return true;
}
return false;
}
indexOf方法实现如下:
/**
* Returns the index of the first occurrence of the specified element in
* this vector, searching forwards from {@code index}, or returns -1 if
* the element is not found.
* More formally, returns the lowest index {@code i} such that
* <tt>(i >= index && (o==null ? get(i)==null : o.equals(get(i))))</tt>,
* or -1 if there is no such index.
*
* @param o element to search for
* @param index index to start searching from
* @return the index of the first occurrence of the element in
* this vector at position {@code index} or later in the vector;
* {@code -1} if the element is not found.
* @throws IndexOutOfBoundsException if the specified index is negative
* @see Object#equals(Object)
*/
public synchronized int indexOf(Object o, int index) {
if (o == null) {
for (int i = index ; i < elementCount ; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = index ; i < elementCount ; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
removeElementAt方法实现如下:
/**
* Deletes the component at the specified index. Each component in
* this vector with an index greater or equal to the specified
* {@code index} is shifted downward to have an index one
* smaller than the value it had previously. The size of this vector
* is decreased by {@code 1}.
*
* <p>The index must be a value greater than or equal to {@code 0}
* and less than the current size of the vector.
*
* <p>This method is identical in functionality to the {@link #remove(int)}
* method (which is part of the {@link List} interface). Note that the
* {@code remove} method returns the old value that was stored at the
* specified position.
*
* @param index the index of the object to remove
* @throws ArrayIndexOutOfBoundsException if the index is out of range
* ({@code index < 0 || index >= size()})
*/
public synchronized void removeElementAt(int index) {
modCount++;
if (index >= elementCount) {
throw new ArrayIndexOutOfBoundsException(index + " >= " +
elementCount);
}
else if (index < 0) {
throw new ArrayIndexOutOfBoundsException(index);
}
int j = elementCount - index - 1;
if (j > 0) {
System.arraycopy(elementData, index + 1, elementData, index, j);
}
elementCount--;
elementData[elementCount] = null; /* to let gc do its work */
}
实现原理很简单,根据要删除的对象找到它的下标i,然后根据这个下标删除第i个元素即可。除了加上了锁,以及一些代码风格的,与ArrayList没有区别。
其他的方法不作仔细分析,到这里可以明确的是,对于Arraylist与Vector最常用的方法,以及其实现原理,以及他们之间的差异,都已经非常清楚了。