1. 前言
數(shù)組是一種重要的數(shù)據(jù)結構,可以利用它作為基礎來實現(xiàn)很多復雜的數(shù)據(jù)結構。因此我們要深入理解數(shù)組的存儲原理和特點,熟悉它的常用操作,重點掌握它在聲明、查詢、存儲、復制等操作時的效率,便于我們在今后的學習和使用中能夠充分利用數(shù)組的優(yōu)勢。
2. 什么是數(shù)組?
數(shù)組(Array)是一種簡單的復合數(shù)據(jù)類型,它是一組有序數(shù)據(jù)的集合。數(shù)組根據(jù)維度可以分為一維數(shù)組、二維數(shù)組和多維數(shù)組。
因為數(shù)據(jù)結構的本質就是存放數(shù)據(jù)的容器,所以我們可以找到它們在生活中的很多原型。如果把冰箱比作計算機存儲的話,我們最常見到的雞蛋盒就是數(shù)組的最佳模型了,他們的很多特點甚至都非常相似。
3. 數(shù)組的特點
數(shù)組在內(nèi)存中是存儲在一段連續(xù)的空間中,這使得數(shù)組在讀取的時候非常高效。我們在冰箱中用雞蛋盒存放雞蛋也是利用了冰箱里一塊完整連續(xù)的空間,便于我們存取雞蛋的時候快速準確而不是把雞蛋散落在各個角落里。但是雞蛋盒有一個缺點就是我們買來的時候它的容量是固定的,如果裝滿了就不能再擴容了,數(shù)組也是一樣,在創(chuàng)建的時候必須確定長度并且不能變更,使得數(shù)組不適用于元素需要頻繁增加和刪除的場景??偨Y起來數(shù)組和雞蛋盒同時具有以下三大特點:
- 一致性:數(shù)組中的每個元素都具有相同的數(shù)據(jù)類型,我們生活中一般也只會在雞蛋盒中放置雞蛋而不會摻雜草莓;
- 有序性:數(shù)組中的元素是有序的,并且可以用唯一下標來訪問;雞蛋盒也是有序的,甚至我們也可以對雞蛋盒中的格子編號來明確快速地指出是哪一個格子的雞蛋有裂紋;
- 不可變性:數(shù)組在初始化的時候長度一旦確定,就不可以再變更;而雞蛋盒子一旦需要變更容量,我們就只能買一個新的盒子了。
4. 數(shù)組的幾個常用操作
了解了數(shù)組的特點,我們來看下怎么使用他們。
4.1 聲明和賦值
- 只聲明數(shù)組,并未在堆內(nèi)存中開辟空間
// 數(shù)據(jù)類型 [] 數(shù)組名稱 = null;
int [] array = null;
- 聲明數(shù)組的同時開辟空間
// 數(shù)據(jù)類型 [] 數(shù)組名稱 = new 數(shù)據(jù)類型[長度];
int [] array = new int[3];
- 聲明數(shù)組的同時開辟空間、同時給數(shù)組插入元素
//數(shù)據(jù)類型 [] 數(shù)組名稱 = new 數(shù)據(jù)類型[]{元素1,元素2...};
int [] array = new int[]{1,2,3};
//數(shù)據(jù)類型 [] 數(shù)組名稱 = {元素1,元素2...}(上一種方法的縮寫,不建議);
int [] array = {1,2,3};
- 多維數(shù)組的聲明方式
//數(shù)據(jù)類型 對象數(shù)組[][] = new 數(shù)據(jù)類型[一維長度][二維長度];
int array[][] = new int[3][5];
聲明數(shù)組有以上幾種方式,因為數(shù)組是引用數(shù)據(jù)類型,在棧內(nèi)存聲明、在堆內(nèi)存中開辟連續(xù)的定長空間,所以我們可以在下圖中看到第二種方式聲明數(shù)組的時候,其實是分步完成的,第一步開辟了一個長度為3的數(shù)組空間,第二步分別為數(shù)組的三個元素賦值。
4.2 遍歷和讀取
數(shù)組有一個屬性 length 表示數(shù)組的長度,使得我們可以很方便的遍歷數(shù)組。這個長度在開辟空間的時候就已經(jīng)固定了,就像雞蛋盒子的容量一樣,而且不論格子里存放元素的數(shù)量是多少,它的容量都是恒定不變的。
// 聲明一個數(shù)組并賦值
int [] array = new int[5];
array[0] = 1;
array[1] = 2;
array[2] = 3;
array[3] = 4;
array[4] = 5;
//遍歷方式1:
for (int i = 0; i < array.length; i++) {
System.out.println(array[i]);
}
//遍歷方式2:
for(int a:array){
System.out.println(a);
}
4.3 復制擴容
前面我們提到,盒子里的雞蛋放不下只能去購買新的盒子,數(shù)組也是一樣,容量不夠的時候只能去創(chuàng)建新的數(shù)組,并且將原有數(shù)組中的元素按照原來的序號復制到新的數(shù)組中去。
//為長度為5的原數(shù)組擴容一倍
//第一步:聲明初始數(shù)組
int [] array = new int[]{1,2,3,4,5};
//第二步:聲明一個容量為原始數(shù)組2倍的新數(shù)組
int [] newArray = new int[array.length*2];
//第三步:將元素復制到新數(shù)組中
for ( int i = 0; i < array.length; i++) {
newArray[i] = array[i]; `
}
說到這里我們還要提一個細節(jié),就是數(shù)組的空間是連續(xù)的,不等同于數(shù)組的元素是連續(xù)的,我們可以在數(shù)組中的任意位置存放或不存放元素,就像我們可以在雞蛋盒子的任何一個格子里放或不放雞蛋,都不影響數(shù)組本身的特性。
4. java.util.Arrays
我們對數(shù)組進行的操作其實很多都被封裝在一個叫做 Arrays 的工具類中了,它為數(shù)組封裝了一些常用的靜態(tài)方法,使得我們可以輕松的對數(shù)組實現(xiàn)查詢、排序、填充等操作,大家可以通過查看源代碼或 API 來了解他們的使用方法。前面我們講到的復制擴容數(shù)組就可以用 Arrays.copyOf (原數(shù)組名,新數(shù)組長度) 方法來輕松實現(xiàn)。
這里我們還要單獨提一下 asList () 方法,這個方法可以將數(shù)組轉換成 ArrayList,使我們有更多的手段來處理數(shù)組,比如追加元素、刪除元素、判斷某個元素是否在數(shù)組中等等,解決了數(shù)組長度不能改變等特性給我們帶來的不便。
//聲明一個數(shù)組并賦值
Integer[] array = new Integer[5];
array[0] = 1;
array[1] = 2;
array[2] = 3;
array[3] = 4;
array[4] = 5;
ArrayList<Integer> arrayList = new ArrayList<Integer>(Arrays.asList(array));
System.out.println(arrayList);
輸出:[1,2,3,4,5]
boolean isExist = Arrays.asList(array).contains(1);
System.out.println(isExist);
輸出:true
5. 數(shù)組的應用與思考
根據(jù)數(shù)組的特點我們可以發(fā)現(xiàn),數(shù)組的使用場景多是在讀取頻繁,增減較少最好是不需要增減的場合,在初始化數(shù)組的時候能夠確定元素的最大個數(shù),比如以下場景:
- 存儲某班級學生的語文成績時可以使用數(shù)組,元素長度固定幾乎不需要增減,讀取高效
- 導入EXCEL模板數(shù)據(jù)的時候可以使用二維數(shù)組來儲存臨時數(shù)據(jù),充分利用了數(shù)組讀取效率高的特性
對于動態(tài)增加和減少元素的場景,我們可以使用剛剛提到的 ArrayList,后面的章節(jié)我們會對這部分內(nèi)容做詳細介紹。
這里分享一個有意思的小插曲,我在制做上面圖片敲代碼的時候手誤寫了這樣一行代碼,小伙伴們可以結合第二行和第三行的執(zhí)行結果來解釋一下第一行代碼的實現(xiàn)結果。
int [] array = new int[]{};
System.out.println(array.length);
array[0] = 1;
6. 小結
本節(jié)我們知道了數(shù)組是一組有序數(shù)據(jù)的集合,它的特點一致性、有序性和不可變性,因此它的讀取效率高,增加或刪除元素的效率低。此外我們要熟練掌握數(shù)組的聲明方法和基本操作,作為一個基本數(shù)據(jù)結構,了解它的原理和擴容方式還將有助于我們后面鏈表等知識的學習。