想了解更多數據結構以及算法題,可以關注微信公衆號“數據結構和算法”,每天一題爲你精彩解答。
基礎知識
數組是具有相同類型的數據的集合,也就是說數組的所有元素的類型都是相同的,在所有的數據結構中,數組算是最常見也是最簡單的一種數據結構,我們最常見的也就是一維數組,當然還有二維,三維……,數組需要先聲明才能使用,數組的大小一旦確定就不可以在變了。比如我們聲明一個長度爲10的數組
1 int[] array = new int[10];
數組的下標是從0開始的,比如上面數組的第一個元素是array[0],最後一個元素是array[9]。
我們還可以在聲明的時候直接對他進行初始化,比如
1 int[] array = new int[]{1, 2, 3};
上面我們聲明瞭一個長度爲3的數組。
源碼分析
操作數組的類我們常見的估計也就是ArrayList了,他對數組的操作非常簡單,所有的數據都會存放到這個數組中
1 transient Object[] elementData;
我們來看一下他常見的幾個方法,首先是get方法
1 public E get(int index) {
2 if (index >= size)
3 throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
4
5 return (E) elementData[index];
6 }
首先判斷是否越界,如果越界直接拋異常,否則就根據他的下標從數組中直接返回,在看一下他的set方法
1 public E set(int index, E element) {
2 if (index >= size)
3 throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
4
5 E oldValue = (E) elementData[index];
6 elementData[index] = element;
7 return oldValue;
8 }
和get方法一樣,也是先判斷是否越界,然後再操作,代碼比較簡單,我們再來看一個add方法
1 public void add(int index, E element) {
2 if (index > size || index < 0)
3 throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
4
5 ensureCapacityInternal(size + 1); // Increments modCount!!
6 System.arraycopy(elementData, index, elementData, index + 1,
7 size - index);
8 elementData[index] = element;
9 size++;
10 }
這裏也是先判斷是否越界,然後再判斷是否需要擴容,最後在操作,接着我們來看一下ensureCapacityInternal方法
1 private void ensureCapacityInternal(int minCapacity) {
2 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
3 minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
4 }
5
6 ensureExplicitCapacity(minCapacity);
7 }
8
9 private void ensureExplicitCapacity(int minCapacity) {
10 modCount++;
11
12 // overflow-conscious code
13 if (minCapacity - elementData.length > 0)
14 grow(minCapacity);
15 }
他的默認初始化大小是10
1 private static final int DEFAULT_CAPACITY = 10;
上面代碼第13行,如果我們需要的空間大於數組長度的時候,說明數組不夠用了,要進行擴容,就會執行下面的grow方法,我們來看一下grow方法的代碼
1 private void grow(int minCapacity) {
2 // overflow-conscious code
3 int oldCapacity = elementData.length;
4 int newCapacity = oldCapacity + (oldCapacity >> 1);
5 if (newCapacity - minCapacity < 0)
6 newCapacity = minCapacity;
7 if (newCapacity - MAX_ARRAY_SIZE > 0)
8 newCapacity = hugeCapacity(minCapacity);
9 // minCapacity is usually close to size, so this is a win:
10 elementData = Arrays.copyOf(elementData, newCapacity);
11 }
代碼也比較簡單,擴容的時候在第4行還會增加一半的大小,比如原來數組大小是10,第一次擴容後會是15。在ArrayList中無論使用add還是remove都會使用這樣一個方法
1 System.arraycopy(elementData, index+1, elementData, index,
2 numMoved);
這說明對數組的查找是比較方便的,但對數組的增刪就沒那麼方便了,因爲數組是一塊連續的內存空間,如果在前面增加和刪除,都會導致後面元素位置的變動。
ArraList是線程不安全,如果使用線程安全的可以用Vector,還有一個線程安全的類
CopyOnWriteArrayList,他只在add和remove的時候,也就是修改數據的時候會先synchronized,在get的時候沒有,我們來看一下代碼
1 private E get(Object[] a, int index) {
2 return (E) a[index];
3 }
我們再來看一下他的add方法
1 public boolean add(E e) {
2 synchronized (lock) {
3 Object[] elements = getArray();
4 int len = elements.length;
5 Object[] newElements = Arrays.copyOf(elements, len + 1);
6 newElements[len] = e;
7 setArray(newElements);
8 return true;
9 }
10 }
他不像ArrayList每次擴容的時候,size都會增加一半,他是每次add一個元素的時候size只會加1,同理remove的時候size只會減1。