單個的數組變量可以引用一個大的數據集合。
在程序執行過程中,經常需要存儲大量的數據,例如,假設需要讀取某科100位學員的成績,計算它們的平均成績,然后找出有多少個學員成績大於平均值。首先,程序需要讀入這些數並且計算它們的平均值,然后將每個數與平均值進行比較判斷它是否大於平均值。為了完成這個任務,必須將全部的數據存儲到變量中。必須聲明100個變量,並且重復書寫100次幾乎完全相同的代碼。這樣編寫程序的方式是不太現實的,那么該如何解決這個問題呢?
這就需要高效有條理的方法——數組:數組是編程語言中最常見的一種數據結構,可以用它來存儲一個元素個數固定且元素類型相同的有序集,每個數組元素存放一個數據,通常可通過數組元素的索引來訪問數組元素,包括為數組元素賦值和取出數組元素的值。在現在這個例子中,可以將所有的100位學員的成績存儲在一個數組中,並且通過一個數組變量訪問它。
一旦數組被創建,它的大小是固定的。使用一個數組引用變量,通過下標來訪問數組中的元素
數組是用來存儲數據的集合,但是,java的數組要求所有的數組元素具有相同的類型。因此,在一個數組中,數組元素的類型是唯一的,即一個數組里只能存儲一種數據類型的數據,而不能存儲多種數據類型的數據。無須聲明單個變量,例如:score0, score1,...score99,只要聲明一個數組變量scores,並且用scores[0], scores[1],...scores[99]來表示單個變量(成績)。
1、聲明數組
為了在程序中使用數組,必須聲明一個引用數組的變量,並指明數組的元素類型
語法:elementType[] arrayName;(數據類型[] 數組引用變量)
elementType可以是任意數據類型,但是數組中所有的元素都必須具有相同的數據類型。
注意:因為java是面向對象的語言,而類與類之間可以支持繼承關系,這樣可能產生一個數組里可以存放多種數據類型的假象(參考后面合唱團例子)。例如有一個水果數組,要求每個數組元素都是水果,實際上數組元素既可以是蘋果,也可以是是banana(蘋果、香蕉都繼承了水果,都是一種特性的水果),但這個數組的數組元素的類型還是唯一的,只能是水果類型。
從上面語法中看出,數組也是一種數據類型,它本身是一種引用數據類型。例如int是一個基本數據類型,但int[]就是一種引用數據類型。
聲明數組時不能指定數組的長度
2、數組初始化
不同於基本數據類型變量的聲明,聲明一個數組變量時並不在內存中給數組分配任何空間。數組是一種引用類型的變量,因此使用它定義一個變量時,僅僅表示定義了一個引用變量(也就是定義了一個指針),這個引用變量還沒有指向任何有效的內存,因此定義數組時不能指定數組的長度。而且由於定義數組只是定義了一個引用變量,並未指向任何有效的內存空間,所以還沒有內存空間來存儲數組元素,因此這個數組也不能使用,只有對數組進行初始化后才能使用(如果變量不包含對數組的引用,那么這個變量的值為null。除非數組已經被創建,否則不能給它分配任何元素)。
初始化數組有兩種方式
1)靜態初始化:初始化數組和給數組賦值同時完成,在開辟數組的空間時依據已給定的元素個數來開辟相應的空間數。
a.靜態初始化的第一種方式:
elementType[] arrayName = new elementType[]{value0,value1,value2,...,valuek};
b.靜態初始化的第二種方式:Java有一個簡捷的標記,稱作數組初始化語法,它使用下面的語法將聲明數組、創建數組和初始化數組結合到一個語句中:
elementType[] arrayName={value0,value1,value2,...,valuek};(元素類型[] 數組引用變量={值1,值2,...,值k};)
說明數組在定義時就完成了空間的開辟和賦值;注意:使用該語法時,必須聲明、創建和初始化數組都放在一條語句中。將他們分開會產生語法錯誤。因此,下面的語句是錯誤的:。
int a[];
a = {10,20,30};//這不行
2)動態初始化:只告知數組需要幾個存儲空間,但是並未決定要存哪些值,由系統為每個元素指定初始值。語法格式如下:
arrayName = new type[length];
//數組的聲明和初始化同時完成,使用動態初始化語法 int[] prices = new int[5]; //數組的聲明和初始化同時完成,初始化數組時元素的類型是定義數組時元素的子類 Object[] books = new String[5];
注意:a.做動態初始化時必須指明數組的元素個數;
b.數組一旦初始化,其長度是不可變的。
//創建數組及初始化代碼 double[] myList = new double[10]; myList[0] = 5.6; myList[1] = 4.5; myList[2] = 3.3; myList[3] = 13.2; myList[4] = 4.0; myList[5] = 34.33; myList[6] = 34.0; myList[7] = 45.45; myList[8] = 99.993; myList[9] = 11123;
下圖展示了這個數組
3、數組大小和默認值
當數組分配空間時,必須指定該數組能夠存儲的元素個數,從而確定數組大小。創建數組之后就不能在修改它的大小。可以使用arrayName.length得到數組的大小。例如:prices.length為5.
當創建數組后,它的元素被賦予默認值,數值型基本數據類型默認值為0,char型的默認值為'\u0000‘,boolean型的默認值為false,引用數據類型默認值為null.
4、訪問數組元素
使用“數組名[下標]”的方式,數組下標從0開始,到arrayName.length - 1結束。
數組中的每個元素都可以使用下面的語法表示,稱為下標變量(indexed variable)
arrayName[index];(數組引用變量[下標];)
例如:prices[4]表示數組prices的最后一個元素。
創建數組后,下標變量與正常變量的使用方法相同。例如:下面代碼將prices[0]和prices[1]的值相加賦給prices[2].
prices[2] = prices[0] + prices[1];
下面的循環是將0付給prices[0],1賦給prices[1]....4賦給prices[4];
for(int i = 0; i < prices.length; i++){ prices[i] = i; }
5、處理數組
處理數組元素時,經常會用到for循環,理由有以下兩點:
1).數組中的所有元素都是同一類型的,可以使用循環以同樣的方式反復處理這些元素。
2).由於數組的大小是已知的,所以很自然地使用for循環。
假如創建如下數組
double[] list = new double[10];
下面是一些處理數組的例子:
1)使用輸入值初始化數組:循環使用用戶輸入的數值初始化數組。
/** * 使用輸入值初始化數組:循環使用用戶輸入的數值初始化數組。 */ public static void inputInitArray() { //聲明創建了大小為10的double型數組 double[] list = new double[10]; Scanner input = new Scanner(System.in); System.out.println("請輸入" + list.length + "個值:"); for(int i = 0; i < list.length; i++) list[i] = input.nextDouble(); input.close(); printArray(list); }
2)使用隨機數初始化數組
/** * 使用隨機數初始化數組 */ public static void randomInitArray() { double[] list = new double[10]; //Math.random()---->[0,1) for(int i = 0; i < list.length; i++) list[i] = Math.random() * 100; printArray(list); }
3)顯示數組,打印數組中的每一個元素
/** * 打印數組中的每個元素 * @param arr double型數組 */ public static void printArray(double[] arr) { //第一種方式:使用for循環 System.out.print("["); for(int i = 0; i < arr.length; i++) System.out.print(arr[i] + (i == arr.length - 1 ? "]\r\n" : ", ")); //第二種形式:使用Arrays工具類完成打印 System.out.println("========================="); System.out.println(Arrays.toString(arr)); }
4)對所有元素進行求和,使用名為total的變量存儲和。total的值初始化為0.
/** * 對double數組中所有元素進行求和 * @param arr double數組 * @return 和 */ public static double sum(double[] arr) { double total = 0; for(int i = 0; i < arr.length; i++) total += arr[i]; return total; }
5)找出最大值
使用名為max的變量存儲最大值。將max的值初始化為list[0],為了找出數組list中的最大元素,將每個元素與max比較,如果該元素大於max,則更新max
/** * 獲取double數組中最大元素 * @param arr double數組 * @return double數組中最大元素 */ public static double getMax(double[] arr) { double max = arr[0]; for(int i = 1; i < arr.length; i++) { if(max < arr[i]) max = arr[i]; } return max; }
6) 找出最大元素的最小下標值
/** * 找出最大元素的最小下標值(如果數組中含有多個最大元素,那么找出最大元素第一次出現的位置) * @param arr double數組 * @return double數組中最大元素的最小索引 */ public static int getMaxElementIndex(double[] arr) { double max = arr[0]; int index = 0; for(int i = 1; i < arr.length; i++) { if(max < arr[i]) { max = arr[i]; index = i; } } return index; }
7)隨機打亂,在很多應用中,需要對數組中的元素進行任意的重新排序(如洗牌),這稱作打亂(shuffling)。為了完成這種功能,針對每個元素list[i],隨意產生一個下標j,然后將list[i]和list[j]互換 .
/** * 隨機打亂:對數組中的元素進行任意的重新排序, * 針對每個元素arr[i],隨意產生一個下標j,然后將arr[i]和arr[j]進行互換 * @param arr 要打亂的數組 */ public static void shuffing(double[] arr) { for(int i = 0; i < arr.length; i++) { int j =(int) (Math.random() * (i + 1)); double temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } System.out.println(Arrays.toString(arr)); }
8)移動元素:向左或者向右移動元素,這里的例子就是將元素向左移動一個位置:
/** * 移動元素:向左或者向右移動元素,這里的例子就是將數組中每個元素依次向左移動一個位置 * 將第一個元素值放在最后一個元素 * @param arr */ public static void moveElement(double[] arr) { double temp = arr[0]; for(int i = 1; i < arr.length; i++) { arr[i - 1] = arr[i]; } arr[arr.length - 1] = temp; }
9)簡化編碼:對於某些任務來說,數組可以極大簡化編碼,比如前面的生肖年。
String[] years = {"猴","雞","狗","豬","鼠","牛","虎","兔","龍","蛇","馬","羊"}; System.out.println(year + "年的生肖為:" + years[year % 12]);
6、foreach循環
java支持一個簡便的for循環,稱為foreach循環,即不使用下標變量就可以順序地遍歷整個數組:
for(double e : list){ System.out.println(e); }
可以讀作:對list中每個元素e進行以下操作。注意,變量e必須聲明為與list中元素相同的數據類型。
通常,foreach的語法為:
for(elementType element : arrayName){
//Process the element
}
但是 ,當需要使用其它順序遍歷數組或改變數組中的元素時,還是必須使用下標變量
警告:越界訪問數組時經常會出現的程序設計錯誤,會拋出一個運行錯誤ArrayIndexOutOfBoundsException。為了避免錯誤的發生,在使用時應該確保所使用的下標不超過數組.length - 1.
數組的索引下標是從0開始的(下標為0表示第一個元素)
7、示例:分析數字
回到開篇中讀取100個數,計算它們的平均值,然后找出有多少個數大於平均值。為了更加靈活地處理任意數目的輸入,我們讓用戶給出輸入的個數,而不是將其固定為100.
import java.util.Scanner; public class AnalyzeNumber { public static void main(String[] args) { Scanner input = new Scanner(System.in); System.out.println("請輸入要存儲的個數"); int n = input.nextInt(); double[] numbers = new double[n]; double sum = 0; System.out.println("請輸入您想存儲的數字"); for(int i = 0;i < numbers.length;i++){ numbers[i] = input.nextDouble(); sum += numbers[i]; } double avg = sum / n; System.out.println("平均值為:"+avg); for (int i = 0; i < n; i++) { if(numbers[i] > avg){ System.out.println(numbers[i]); } } input.close(); } }
8:數組的復制(*)
要將一個數組中的內容復制到另外一個數組中,你需要將數組的每一個元素復制到另外一個數組中。
在程序中經常需要復制一個數組或數組的一部分。在這種情況下,你可能會嘗試使用賦值語句(=),如下所示:
list2 = list1;
該語句並不能將list1引用的數組內容復制給list'2,而只是將list1的引用值復制給了list2.在這條語句后,list1和list2都指向同一個數組,如下圖所示。list2原先引用的數組不能再引用,它就變成了垃圾,會被java虛擬機自動收回(垃圾回收)。
在Java中,可以使用復制語句復制基本數據類型的變量,但不能復制數組。將一個數組變量賦值給另一個數組變量,實際上是將一個數組的引用賦值給另一個變量,使兩個變量都指向相同的內存地址。
復制數組有三種方法:
- 使用循環語句逐個地復制數組的元素。
int[] sourceArray = {2,5,8,10,200,-20}; int[] targetArray = new int[sourceArray.length]; for(int i = 0; i < sourceArray.length; i++) targetArray[i] = sourceArray[i];
- 使用System類中的靜態方法arraycopy。
System.arraycopy(sourceArray,srcPos,targetArray,tarPos,length); 其中,參數srcPos和tarPos分別表示在源數組sourceArray和目標數組targetArray中的起始位置,從sourceArray復制到targetArray中的元素個數由參數length指定。例如: System.arraycopy(sourceArray,0,targetArray,0,sourceArray.length);
- 使用clone方法復制數組。
示例:一副牌
從一副52張的牌中隨機挑出4張牌。所有的牌可以用一個名為deck的數組表示,這個數組從0到51的初始值來填充:
int[] deck = new int[52]; //初始化牌 for(int i = 0; i < deck.length; i++) deck[i] = i;
牌號從0到12、13到25、26到38以及39到51分別表示13張黑桃、13張紅桃,13張方塊、13張梅花,cardNumber / 13決定牌的花色,而cardNumber % 13 決定是具體花色中的哪張牌。在洗牌(打亂數組deck)后,從deck中選出前4張牌
52張牌存儲在一個名為deck的數組中
CardNumber標識一張牌的花色和等級數字
/** * 從一副52張的牌中隨機挑出4張牌 * @author Adan * */ public class DeckDemo { public static void main(String[] args) { //聲明一個52個元素空間大小的int數組來表示52張牌 int[] deck = new int[52]; for(int i = 0; i < deck.length; i++) deck[i] = i; //洗牌(隨機打亂) for(int i = 0; i < deck.length; i++) { int index = (int)(Math.random() * deck.length); int temp = deck[i]; deck[i] = deck[index]; deck[index] = temp; } //定義一個代表撲克牌花色的數組 String[] suits = {"♠","♥","♣","♦"}; //同理,定義牌號的數組 String[] ranks = {"A","2","3","4","5","6","7","8","9","10","J","Q","K"}; for(int i = 0; i < 3;i++) { String suit = suits[deck[i] / 13];//取得花色 String rank = ranks[deck[i] % 13]; System.out.println(suit + " " + rank); } } }
將數組傳遞給方法
當一個數組傳遞給方法時,數組的引用被傳遞給方法
public static double getMax(double[] arr) { double max = arr[0]; for(int i = 1; i < arr.length; i++) { if(max < arr[i]) max = arr[i]; } return max; }
Java使用按值傳遞(pass-by-value)的方式將實參傳遞給方法。傳遞基本數據類型變量的值與傳遞數組值有很大的不同。
- 對於基本數據類型參數,傳遞的是實參的值。
- 對於數組類型參數,參數值是數組的引用,給方法傳遞的這個引用。從語義上來講,最好的描述就是參數傳遞的是共享信息(pass-by-sharing),既方法中的數組和傳遞的數組是一樣的。所以,如果改變方法中的數組,將會看到方法外的數組也變化了。
從方法中返回數組
/** * 從方法返回一個數組(反轉后的數組) * @param list 源數組 * @return 反轉后的數組 */ public static int[] reverse(int[] list) { int[] result = new int[list.length]; for(int i = 0,j = result.length - 1; i < list.length; i++,j--) result[j] = list[i]; return result; }
示例:統計每個字母出現的次數
隨機生成100個小寫字母並將其放入到一個字符數組中,對該數組中每個字母出現的次數進行統計。
public class StatisticsLetterCount { /** * 創建一個大小為100的字符數組,用於存放隨機生成的小寫字母 * @return 字符數組 */ public static char[] createLetters() { char[] letters = new char[100]; for(int i = 0; i < letters.length; i++) { letters[i] = generateRandomLowerLetter(); } return letters; } public static char generateRandomLowerLetter() { return (char)('a' + Math.random() * ('z' - 'a')); } public static void showLetterCount(char[] letters) { int[] times = new int[26];//每個元素初始值為0,times[0]代表a出現的次數,times[25】代表z出現的次數 for(int i = 0; i < letters.length; i++) times[letters[i] - 'a']++; for(int i = 0; i < times.length; i++) System.out.print((char)('a' + i) + ":" + times[i] + ((i + 1) % 10 == 0 ? "\r\n" : "\t")); } public static void main(String[] args) { showLetterCount(createLetters()); } }
可變長參數列表
具有相同類型的可變長參數可以傳遞給方法,可變參數的本質為數組
public static int sum(int ... nums) { if(nums == null) return 0; int sum = 0; for(int num : nums) sum+= num; return sum; }
注意:一個方法只能有一個可變參數且只能出現在最后位置。
數組的查找
- 線性查找
/** * 線性查找關鍵字元素key在數組中的索引 * 從數組的第一個元素依次的比較,如果該元素==關鍵字元素,返回該元素對應的索引。 * @param key 關鍵字 * @param list 數組 * @return 如果查找到該元素,就返回該元素對應的索引,沒有找到返回-1 */ public static int linearSearch(int key,int[] list) { for(int i = 0; i < list.length; i++) { if(list[i] == key) return i; } return -1; }
- 二分查找
/** * 折半查找 * 前提:數組是一個有序數組 * 每次折半取中間元素 * 1.如果關鍵字小於中間元素,只需要在數組的前一半進行查找 * 2.如果關鍵字大於中間元素,只需要在數組的后一半進行查找 * 3.相等,就返回mid */ public static int binarySearch(int key, int[] list) { int low = 0; int high = list.length - 1; while(high >= low) { System.out.println("-----"); int mid = (low + high) >>> 1; if(key < list[mid]) high = mid - 1; else if(key > list[mid]) low = mid + 1; else return mid; } return -low - 1; }
數組的排序
- 選擇排序
package edu.uestc.avatar; import java.util.Arrays; public class ArraySort { /** * 使用選擇排序將數組中的元素進行升序排列 * 首先找到數組中最小的數,然后將它和第一個元素進行交換 * 在剩下的數中找到最小數,然后將它和第二個元素進行交換 * 依次類推,直到數列中只剩下一個元素 * @param array 要排序的整數數組 */ public static void selectionSort(int[] array) { for(int i = 0; i < array.length - 1; i++) { int currentMin = array[i]; int currentMinIndex = i; for(int j = i + 1; j < array.length; j++) { if(currentMin > array[j]) { currentMin = array[j]; currentMinIndex = j; } } if(currentMinIndex != i) { array[currentMinIndex] = array[i]; array[i] = currentMin; } } } public static void main(String[] args) { int[] list = {40, 60, 75, 72, 61, 73, 18, 35, 95, 70}; selectionSort(list); System.out.println(Arrays.toString(list)); } }
- 冒泡排序
Arrays類(java.util.Arrays)
1.Arrays類包含一些實用的方法用於常見的數組操作,比如排序和查找
2.Arrays類包含各種各樣的靜態方法,用於實現數組的排序和查找、數組的比較和填充數組元素,以及返回數組的字符串表示。這些方法都有對所有基本類型的重載方法。
3.可以使用sort或parallelSort方法對整個數組或部分數組進行排序。
4.可以采用二分查找法(binarySearch方法)在數組中查找關鍵字。數組必須提前按升序排列好。如果數組中不存在關鍵字,方法返回 -(插入點下標+1)。
5.可以采用equals方法檢測兩個數組是否相等。如果他們的內容相同,那么這兩個數組相等。
6.可以使用fill方法填充整個數組或部分數組。
7.可以是同toString方法來返回一個字符串,該字符串中代表了數組中的所有元素。這是一個顯示數組中所有元素的快捷和簡便的方法。
命令行參數
課后作業(練習)
1.有一個數組:8,4,2,1,23,344,12
1)循環輸出數組的值——使用三種方式。
2)求數組中所有數值的和。
2.猜數游戲:從鍵盤中任意輸入一個數據,判斷數組中是否包含此數,要求每一個功能獨立成方法。
3.找出兩個數組中的差異元素並存入一個新的數組並排序,假設每個數組內部都沒有重復元素。
4.取1-100的10個隨機數存入數組中,要求數據不能重復
5. 設計一個方法,將數組中的數據打亂順序
6.n(自定義輸入)個人圍成一圈,並依次編號1-n,從編號為1的人開始按順時針方向每隔一人選出一個,剩下的人重新圍成一圈,如此循環,直到只剩下1,2人,如果你想成為這兩個幸運兒,問:最開始你應該站在什么位置.
7.給定一個整數數組,例如{6,4,7,2,5,8}和一個數字,例如10,請設計一個函數找出兩個元素(或同一個元素加自身),並且使這兩個數的和為給定數字,並打印出來(提示:先進行數組排序)
8.給定一個含有n個元素的整型數組a例如{1,1,1,2,4,3,3} ,如果某些元素出現的次數為奇數次,則將其輸出:例如1,2,4
9.冒泡排序(下沉排序)
10.打印兩個有序數組中的共同元素