1,常見數據結構-數組

想了解更多數據結構以及算法題,可以關注微信公衆號“數據結構和算法”,每天一題爲你精彩解答。

基礎知識

數組是具有相同類型的數據的集合,也就是說數組的所有元素的類型都是相同的,在所有的數據結構中,數組算是最常見也是最簡單的一種數據結構,我們最常見的也就是一維數組,當然還有二維,三維……,數組需要先聲明才能使用,數組的大小一旦確定就不可以在變了。比如我們聲明一個長度爲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。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章