數組

PS:若是以爲文章有什麼地方寫錯了,哪裏寫得很差,或者有什麼建議,歡迎指點。

1、認識數組

數組是一種線性表數據結構。它用一塊連續的內存空間,來存儲相同類型的一組數據。java

1. 概念的理解

線性表:

顧名思義,線性表就是數據排列成像一條線同樣的結構。每一個線性表上的數據最多隻有前和後兩個方向,數組,鏈表,棧,隊列等都是典型的線性表結構。數組

線性表-圖片來源極客時間

與其相對立的,在非線性表中,數據之間並非簡單的先後關係,像樹,堆,圖等都是典型的非線性表。數據結構

非線性表-圖片來源極客時間

連續的內存空間和相同類型的數據:

即計算機分配連續的內存單元來存儲數據,相同類型的數據即每一個內存單元的大小是相同的。性能

如聲明一個長度爲 5 的 int 類型的數組 int[] arr = new int[5] ,計算機給數組分配了一塊連續的內存空間,其中內存塊的首地址爲 base_address = 0xc0000160e0,每一個內存單元佔 4 個字節:優化

數組內存結構

2. 高效的隨機訪問

數組的一個特色是能夠根據下標隨機訪問數組元素,其時間複雜度爲 O(1),那麼它是如何實現的呢?spa

計算機分配的內存單元存儲數據時,也會爲內存單元分配一個地址,而後能夠經過地址來訪問內存中的數據。由數組的內存空間連續的特性,當須要訪問某個元素時,它會經過下面的尋址公式來計算出該元素存儲的內存地址:code

// 一維數組 arr[n]:
arr[i]_address = base_address + i * data_type_size

// 二維數組 arr[m][n]:
address = base_address + (i * n + j) * data_type_size

其中 data_type_size 表示數組中每一個元素的大小,如在 int 型的數組 arr 中,data_type_size 就爲 4 個字節。隊列

這樣,即可以很快的根據內存地址來讀取數據。圖片

3. 低效的「插入」和「刪除」

在數組中,爲了保持內存數據的連續性,會致使插入、刪除這兩個操做比較低效。內存

例如在 插入操做 中,假設數組的長度爲 n,若咱們要在數組的第 k 個位置插入一個數據,爲了把第 k 個位置騰出來給新的數據,咱們須要將第 k ~ n 這部分的元素都順序地向後挪一位: arr[i] = arr[i-1] 。其時間複雜度爲 O(n)。

而在 刪除操做 中,若咱們要刪除數組的第 k 個元素,爲了內存的連續性,就須要將第 k ~ n 這部分的元素都順序地向前挪一位: arr[i] = arr[i+1] 。其時間複雜度爲 O(n)。

插入和刪除操做的優化

然而在不少咱們不須要考慮數組中元素的有序性,數組只被看成一個存儲數據的集合的時候,爲了不大規模的數據搬移,咱們能夠對插入和刪除操做作一些優化。例如:

  • 若是要將某個數據插入到第 k 個位置,能夠直接將第 k 爲的數據搬移到數組元素的末尾,而後將新的元素值直接賦值給第 k 個元素;
  • 若是要將第 k 個元素刪除,能夠直接將數組的最後一個元素賦值給第 k 個元素,而後刪除最後一個元素便可。

這樣,其時間複雜度就會降爲 O(1) 。

4. 容器與數組的比較

對於數組類型,Java 中的 ArrayList 容器是基於數組實現的,那麼兩者相比各有什麼優勢和適用場景呢?

  1. ArrayList 的優點是方便,適合在通常的業務中使用。它將不少數組的操做細節封裝起來了,如數據的插入、刪除、數組的擴容。ArrayList 沒法存儲基本類型,好比 int、double,須要封裝爲 Integer、Double 類,而自動裝箱/拆箱的操做會有必定的性能消耗;
  2. 相對於容器來講,數組的使用雖然麻煩一點,但它的性能會優於容器,更適合用於底層的開發和追求極致性能的優化。

2、數組擴容及拷貝

1. 數組的擴容

數組是根據固定容量建立的,在必要的時候咱們須要對數組 arr 進行擴容:

// 初始長度爲 10
int[] arr = new int[10];
for (int i = 0; i < arr.length; i++) {
    arr[i] = i+1;
}
...
// 下面決定須要對數組 arr 進行擴容
int[] newArr = new int[arr.length*2];

// 對原數組進行內容拷貝
for (int i = 0; i < arr.length; i++) {
    newArr[i] = arr[i];
}

arr = newArr;

在對數組進行拷貝時除了利用 for 循環遍歷數組元素進行拷貝外,推薦使用更高效的 System.arraycopy() 方法。

2. System.arraycopy() 方法拷貝數組

System.arraycopy() 使用 native 關鍵字修飾,大大加快程序性能,爲 JVM 內部固有方法。它經過手工編寫彙編或其餘優化方法來進行 Java 數組拷貝,這種方式比起直接在 Java 上進行 for 循環或 clone 是更加高效的。數組越大致現地越明顯。

該方法用於從指定源數組中進行拷貝操做,能夠指定開始位置,拷貝指定長度的元素到指定目標數組中。其聲明以下:

public static native void arraycopy(Object src,  int srcPos, Object dest, int destPos, int length);

參數說明:

  1. src:要被複制的數組,
  2. srcPos:被複制的數組開始複製的下標,
  3. dest:目標數組,也就是要把數據放進來的數組,
  4. destPos:從目標數組第幾個下標開始放入數據,
  5. length:被複制的數組中拿幾個數值放到目標數組中;

例,數組 arr 進行擴容:

// 初始長度爲 10
int[] arr = new int[10];
for (int i = 0; i < arr.length; i++) {
    arr[i] = i+1;
}
...
// 下面決定須要對數組 arr 進行擴容
int[] newArr = new int[arr.length*2];
System.arraycopy(arr,0,newArr,0,10); // 對原數組進行內容拷貝
arr = newArr;

絕大部分數組和基於數組實現的容器(ArrayList 等)的擴容都是基於 System.arraycopy() 方法進行操做的。


歡迎您的點贊、收藏和評論!(完)

相關文章
相關標籤/搜索