ArrayList 我們在 java 中再熟悉不過了,記得自己在學(xué)習(xí) Collection 體系的時(shí)候,用得最多的也就是 ArrayList 。幾乎很少用到 Stack 和 LinkedList ,反正只要能用就行,所以剛開始并未過多去了解。但是當(dāng)我們真正了解了其內(nèi)部實(shí)現(xiàn)算法后,在寫代碼的時(shí)候我們就會根據(jù)業(yè)務(wù)邏輯,有意識做一些思考了。還有就是在面試的時(shí)候,我們也經(jīng)常會碰到類似的問題。
一.ArrayList 源碼分析
// 默認(rèn)情況下,數(shù)組的初始化大小
private static final int DEFAULT_CAPACITY = 10;
// 空數(shù)組
private static final Object[] EMPTY_ELEMENTDATA = {};
// 空數(shù)組
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 數(shù)據(jù)
transient Object[] elementData;
// 數(shù)據(jù)大小
private int size;
// 給數(shù)組指定初始化大小
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
// 創(chuàng)建數(shù)組
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
public boolean add(E e) {
// 判斷是否需要擴(kuò)容
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
// 如果數(shù)組為空
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// 如果超出當(dāng)前數(shù)組長度,需要擴(kuò)容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
// 原來數(shù)組的大小
int oldCapacity = elementData.length;
// 默認(rèn)情況下擴(kuò)充為原來的一半
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:
// 創(chuàng)建一個(gè)新數(shù)組并把原來數(shù)組里的內(nèi)容拷貝到新數(shù)組中
elementData = Arrays.copyOf(elementData, newCapacity);
}
public static <T> T[] copyOf(T[] original, int newLength) {
return (T[]) copyOf(original, newLength, original.getClass());
}
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
@SuppressWarnings("unchecked")
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
public E remove(int index) {
// 是否越界
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
modCount++;
// 獲取原來的位置
E oldValue = (E) elementData[index];
// 如果不是最后一個(gè),后面的需要往前面邏
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
// 解除掉對象的 GC root 引用
elementData[--size] = null;
return oldValue;
}
// 通過 native 層去拷貝代碼
// src :原來的數(shù)組
// srcPos:原來數(shù)組的開始位置
// dest:新的數(shù)組
// destPos:新數(shù)組的開始位置
// length:拷貝多少個(gè)
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
通過上面的代碼來分析,ArrayList 其內(nèi)部的實(shí)現(xiàn)方式其實(shí)就是數(shù)組,如果沒指定數(shù)組的大小,那么在第一次添加數(shù)據(jù)的時(shí)候,數(shù)組的初始大小是 10 ,每次當(dāng)不夠用的時(shí)候默認(rèn)會擴(kuò)充原來數(shù)組的 1/2 ,每次擴(kuò)充數(shù)組大小都會涉及到創(chuàng)建新數(shù)組和數(shù)據(jù)的拷貝復(fù)制。而數(shù)組的拷貝和邏動都是由我們的 native 層代碼實(shí)現(xiàn),可是為什么不直接用 java 代碼寫呢?接下來我們?nèi)タ聪?native 層的實(shí)現(xiàn)。
二.實(shí)現(xiàn) Native 層的 ArrayList
#ifndef MYAPPLICATION_ARRAYLIST_H
#define MYAPPLICATION_ARRAYLIST_H
#include <malloc.h>
//------------------類的定義-------------------//
template<class E>
class ArrayList {
public:
// 數(shù)組頭指針
E *array = NULL;
// 數(shù)組長度
int len = 0;
// 數(shù)據(jù)大小
int index = 0;
public:
ArrayList();
ArrayList(int len);
~ArrayList();
ArrayList(const ArrayList &list);
public:
bool add(E e);
int size();
E get(int index);
E remove(int index);
private:
void ensureCapacityInternal(int i);
void grow(int capacity);
};
//------------------類的實(shí)現(xiàn)-------------------//
template<class E>
ArrayList<E>::ArrayList() {
}
template<class E>
ArrayList<E>::ArrayList(int len) {
if (len == 0) {
return;
}
this->len = len;
this->array = (E *) malloc(sizeof(E) * len);
}
template<class E>
ArrayList<E>::~ArrayList() {
if (this->array) {
free(this->array);
this->array = NULL;
}
}
template<class E>
ArrayList<E>::ArrayList(const ArrayList &list) {
this->index = list.index;
this->len = list.len;
// 深拷貝
this->array = (E *) malloc(sizeof(E) * len);
memcpy(this->array,list.array,sizeof(E) * len);
}
template<class E>
E ArrayList<E>::get(int index) {
return this->array[index];
}
template<class E>
int ArrayList<E>::size() {
return this->index;
}
template<class E>
E ArrayList<E>::remove(int index) {
E old_value = this->array[index];
// 計(jì)算出需要邏動的個(gè)數(shù)
int numMoved = this->index - index - 1;
// 從前面不斷的邏動
for (int i = 0; i < numMoved; ++i) {
array[index + i] = array[index + i + 1];
}
this->index -= 1;
return old_value;
}
template<class E>
bool ArrayList<E>::add(E e) {
ensureCapacityInternal(index + 1);
this->array[index++] = e;
return true;
}
// 是否需要調(diào)整當(dāng)前數(shù)組大小
template<class E>
void ArrayList<E>::ensureCapacityInternal(int minCapacity) {
// 當(dāng)前數(shù)組是不是空,或者 len 是不是 0
if (this->array == NULL) {
minCapacity = 10;// 第一次初始化大小
}
// 判斷要不要擴(kuò)容
if (minCapacity - len > 0) {
grow(minCapacity);
}
}
// 擴(kuò)容創(chuàng)建新的數(shù)組
template<class E>
void ArrayList<E>::grow(int capacity) {
// 計(jì)算新數(shù)組大小的長度
int new_len = len + (len >> 1);
if (capacity - new_len > 0) {
new_len = capacity;
}
// 創(chuàng)建新的數(shù)組
E *new_arr = (E *) malloc(sizeof(E) * new_len);
if (this->array) {
// 拷貝數(shù)據(jù)
memcpy(new_arr, array, sizeof(E) * index);
// 釋放原來的內(nèi)存
free(this->array);
}
array = new_arr;
len = new_len;
}
#endif //MYAPPLICATION_ARRAYLIST_H
三.System.arraycopy 源代碼分析
java 中 ArrayList 數(shù)組的拷貝是通過 native 層去實(shí)現(xiàn)的,我看的是 jdk 1.8 的源碼,如果想進(jìn)一步了解其 native 層的實(shí)現(xiàn),我們需要下載 jdk 1.8 的源碼。
打開openjdk\hotspot\src\share\vm\prims\jvm.cpp可以看到一個(gè)方法JVM_ArrayCopy,但是該方法沒有真正實(shí)現(xiàn)復(fù)制的代碼,而是簡單的檢測源數(shù)組和目的數(shù)組是否為空,排除一些異常情況,方法都比較簡單,我們只要順著往下走就行了。
/*
java.lang.System中的arraycopy方法
*/
JVM_ENTRY(void, JVM_ArrayCopy(JNIEnv *env, jclass ignored, jobject src, jint src_pos,
jobject dst, jint dst_pos, jint length))
// 檢查源數(shù)組和目的數(shù)組不為空
if (src == NULL || dst == NULL) {
THROW(vmSymbols::java_lang_NullPointerException());
}
// 進(jìn)行解析轉(zhuǎn)換
arrayOop s = arrayOop(JNIHandles::resolve_non_null(src));
arrayOop d = arrayOop(JNIHandles::resolve_non_null(dst));
assert(s->is_oop(), "JVM_ArrayCopy: src not an oop");
assert(d->is_oop(), "JVM_ArrayCopy: dst not an oop");
//真正調(diào)用復(fù)制的方法
s->klass()->copy_array(s, src_pos, d, dst_pos, length, thread);
}
/*
java.lang.System中的arraycopy方法具體實(shí)現(xiàn)
*/
void ObjArrayKlass::copy_array(arrayOop s, int src_pos, arrayOop d,
int dst_pos, int length, TRAPS) {
//檢測s是數(shù)組
assert(s->is_objArray(), "must be obj array");
//目的數(shù)組不是數(shù)組對象的話,則拋出ArrayStoreException異常
if (!d->is_objArray()) {
THROW(vmSymbols::java_lang_ArrayStoreException());
}
// Check is all offsets and lengths are non negative
//檢測下標(biāo)參數(shù)非負(fù)
if (src_pos < 0 || dst_pos < 0 || length < 0) {
THROW(vmSymbols::java_lang_ArrayIndexOutOfBoundsException());
}
// Check if the ranges are valid
//檢測下標(biāo)參數(shù)是否越界
if ( (((unsigned int) length + (unsigned int) src_pos) > (unsigned int) s->length())
|| (((unsigned int) length + (unsigned int) dst_pos) > (unsigned int) d->length()) ) {
THROW(vmSymbols::java_lang_ArrayIndexOutOfBoundsException());
}
// Special case. Boundary cases must be checked first
// This allows the following call: copy_array(s, s.length(), d.length(), 0).
// This is correct, since the position is supposed to be an 'in between point', i.e., s.length(),
// points to the right of the last element.
//length==0則不需要復(fù)制
if (length==0) {
return;
}
//UseCompressedOops只是用來區(qū)分narrowOop和oop,具體2者有啥區(qū)別需要再研究
//調(diào)用do_copy函數(shù)來復(fù)制
if (UseCompressedOops) {
narrowOop* const src = objArrayOop(s)->obj_at_addr<narrowOop>(src_pos);
narrowOop* const dst = objArrayOop(d)->obj_at_addr<narrowOop>(dst_pos);
do_copy<narrowOop>(s, src, d, dst, length, CHECK);
} else {
oop* const src = objArrayOop(s)->obj_at_addr<oop>(src_pos);
oop* const dst = objArrayOop(d)->obj_at_addr<oop>(dst_pos);
do_copy<oop> (s, src, d, dst, length, CHECK);
}
}
// Either oop or narrowOop depending on UseCompressedOops.
template <class T> void ObjArrayKlass::do_copy(arrayOop s, T* src,
arrayOop d, T* dst, int length, TRAPS) {
BarrierSet* bs = Universe::heap()->barrier_set();
// For performance reasons, we assume we are that the write barrier we
// are using has optimized modes for arrays of references. At least one
// of the asserts below will fail if this is not the case.
assert(bs->has_write_ref_array_opt(), "Barrier set must have ref array opt");
assert(bs->has_write_ref_array_pre_opt(), "For pre-barrier as well.");
if (s == d) {
// since source and destination are equal we do not need conversion checks.
assert(length > 0, "sanity check");
bs->write_ref_array_pre(dst, length);
//復(fù)制的函數(shù)
Copy::conjoint_oops_atomic(src, dst, length);
} else {
// We have to make sure all elements conform to the destination array
Klass* bound = ObjArrayKlass::cast(d->klass())->element_klass();
Klass* stype = ObjArrayKlass::cast(s->klass())->element_klass();
if (stype == bound || stype->is_subtype_of(bound)) {
// elements are guaranteed to be subtypes, so no check necessary
//stype對象是bound,或者stype是bound的子類抑或stype實(shí)現(xiàn)bound接口
bs->write_ref_array_pre(dst, length);
Copy::conjoint_oops_atomic(src, dst, length);
} else {
// slow case: need individual subtype checks
// note: don't use obj_at_put below because it includes a redundant store check
T* from = src;
T* end = from + length;
for (T* p = dst; from < end; from++, p++) {
// XXX this is going to be slow.
T element = *from;
// even slower now
bool element_is_null = oopDesc::is_null(element);
oop new_val = element_is_null ? oop(NULL)
: oopDesc::decode_heap_oop_not_null(element);
if (element_is_null ||
(new_val->klass())->is_subtype_of(bound)) {
bs->write_ref_field_pre(p, new_val);
*p = element;
} else {
// We must do a barrier to cover the partial copy.
const size_t pd = pointer_delta(p, dst, (size_t)heapOopSize);
// pointer delta is scaled to number of elements (length field in
// objArrayOop) which we assume is 32 bit.
assert(pd == (size_t)(int)pd, "length field overflow");
bs->write_ref_array((HeapWord*)dst, pd);
THROW(vmSymbols::java_lang_ArrayStoreException());
return;
}
}
}
}
bs->write_ref_array((HeapWord*)dst, length);
}
// oops, conjoint, atomic on each oop
static void conjoint_oops_atomic(oop* from, oop* to, size_t count) {
assert_params_ok(from, to, LogBytesPerHeapOop);
pd_conjoint_oops_atomic(from, to, count);
}
//檢測是否是k的子類,或者是實(shí)現(xiàn)k接口
bool is_subtype_of(Klass* k) const {
juint off = k->super_check_offset();
Klass* sup = *(Klass**)( (address)this + off );
const juint secondary_offset = in_bytes(secondary_super_cache_offset());
if (sup == k) {
return true;
} else if (off != secondary_offset) {
return false;
} else {
return search_secondary_supers(k);
}
}
static void pd_conjoint_oops_atomic(oop* from, oop* to, size_t count) {
// Do better than this: inline memmove body NEEDS CLEANUP
if (from > to) {
while (count-- > 0) {
// Copy forwards
*to++ = *from++;
}
} else {
from += count - 1;
to += count - 1;
while (count-- > 0) {
// Copy backwards
*to-- = *from--;
}
}
}
視頻地址:https://pan.baidu.com/s/1A-1pG6IwrtR8WrxpZ75gyw
視頻密碼:acw5