Java的面向?qū)ο缶幊?/a>">Java的面向?qū)ο缶幊?/a>
901
2022-05-29
在程序開發(fā)過程中,有時候需要存儲大量的同類型數(shù)據(jù)。例如,存儲一個班級50名學(xué)生的姓名,這時需要定義50個變量來保存姓名數(shù)據(jù),但這種做法太繁瑣了。那么,如何解決這類問題呢?Java語言提供了數(shù)組結(jié)構(gòu),它類似于一個容器,可以批量存儲相同數(shù)據(jù)類型的元素。因此,對于前述學(xué)生成績統(tǒng)計(jì)問題,我們只需要定義一個長度為50的字符串?dāng)?shù)組就可以解決。本章將對數(shù)組的基本概念、定義方式、初始化以及使用等內(nèi)容展開講解。
4.1 一維數(shù)組
一維數(shù)組的邏輯結(jié)構(gòu)是線性表,它是最簡單的數(shù)組。使用一維數(shù)組時,要先定義,然后做初始化,最后才能使用。本節(jié)為大家詳細(xì)講解一維數(shù)組的具體用法。
4.1.1 一維數(shù)組的創(chuàng)建
在Java中使用數(shù)組,一般需要3個步驟:聲明數(shù)組、創(chuàng)建數(shù)組對象空間、為數(shù)組元素賦值。其中,聲明數(shù)組和創(chuàng)建數(shù)組對象空間的語法如下:
數(shù)據(jù)類型[] 數(shù)組名; // 聲明一維數(shù)組,這種是推薦的寫法
數(shù)據(jù)類型 數(shù)組名[]; // 聲明一維數(shù)組的第二種寫法
數(shù)組名 = new 數(shù)據(jù)類型[數(shù)組元素個數(shù)]; // 為數(shù)組對象分配內(nèi)存空間
在上面一維數(shù)組的聲明語句中,“數(shù)據(jù)類型”可以是Java語言中的任意數(shù)據(jù)類型,包括簡單類型和對象類型。“數(shù)組名”是用來訪問數(shù)組的標(biāo)識,作用與變量名類似,在程序中可以通過“數(shù)組名”訪問數(shù)組中的元素,“數(shù)組名”的命名規(guī)則與變量的命名規(guī)則相同。另外,“[ ]”是定義數(shù)組類型的符號,聲明數(shù)組時必須要有這個符號,這個符號可以放在數(shù)據(jù)類型后面,也可以放在數(shù)組名后面。
數(shù)組聲明之后,就需要為數(shù)組對象分配一個固定長度的存儲空間。在為數(shù)組分配內(nèi)存空間的時候,需要使用new運(yùn)算符。通過new運(yùn)算符可以告訴編譯器聲明的數(shù)組存儲什么類型的數(shù)據(jù),以及數(shù)組要存儲的元素個數(shù)。數(shù)組一旦創(chuàng)建長度就固定了,不能再次改變。
針對數(shù)組的聲明和內(nèi)存分配,舉例說明如下:
int[] nums; // 聲明數(shù)組名為nums的整形數(shù)組
nums = new int[10]; // 為nums數(shù)組分配10個存儲整數(shù)的內(nèi)存空間
在這例子中,第1行代碼只是聲明了一個整數(shù)類型的數(shù)組變量nums,但是這個變量沒有指向任何一個數(shù)組對象空間,聲明的數(shù)組變量是在JVM的棧內(nèi)存中分配空間。第2行代碼先通過new運(yùn)算符創(chuàng)建了一個長度為10的整型數(shù)組空間,創(chuàng)建的數(shù)組空間是在JVM的堆內(nèi)存中分配的。接著把數(shù)組對象賦值給nums,也就是將數(shù)組對象的地址存儲到了nums變量中。此時,nums變量就指了這個數(shù)據(jù)對象上,因此通過nums可以訪問到數(shù)組對象空間中的每一個元素。對于數(shù)組聲明和數(shù)組對象內(nèi)存分配,可以參考圖4.1。
(a)只聲明數(shù)組變量但未指向數(shù)組對象空間 (b)聲明數(shù)組并指向數(shù)組對象空間
圖4.1 數(shù)組聲明和內(nèi)存分配
圖4.1中,左圖聲明了一個整型數(shù)組變量nums,這個變量對應(yīng)一個棧內(nèi)存的空間。因?yàn)閚ums沒有指向數(shù)組對象,所以nums存儲的內(nèi)容是null。右圖中在堆內(nèi)存中創(chuàng)建了一個連續(xù)的長度為10的整型數(shù)組對象空間,數(shù)組對象的首地址是0x8A21。右圖中聲明的數(shù)組變量nums存儲了數(shù)組對象的首地址。因此nums變量指向了這個數(shù)組對象,與這個數(shù)組對象有了引用關(guān)聯(lián)。通過nums變量可以用下標(biāo)索引的方式,訪問數(shù)組中的每個元素。
另外,也可以使用一條語句完成數(shù)組的聲明和內(nèi)存分配,語法說明如下:
數(shù)據(jù)類型[] 數(shù)組名 = new 數(shù)據(jù)類型[數(shù)組元素個數(shù)]
使用這種方式會在定義數(shù)組的同時為數(shù)組分配內(nèi)存空間,舉例說明如下:
int[] nums = new int[10]; // 聲明數(shù)組的同時為數(shù)組分配內(nèi)存空間
在這行代碼中,等號左邊的nums是聲明的數(shù)組變量,它指向了右邊使用new運(yùn)算符創(chuàng)建的數(shù)組對象。
知識點(diǎn)撥:在Java中,數(shù)組對象被創(chuàng)建之后,數(shù)組中的元素都具有一個初始的默認(rèn)值。整型數(shù)組的元素默認(rèn)值是0。浮點(diǎn)類型數(shù)組的元素默認(rèn)值是0.0。布爾類型數(shù)組的元素默認(rèn)值是false。字符串和對象等引用類型數(shù)組的元素默認(rèn)值都是null。
4.1.2 數(shù)組元素的分配
在創(chuàng)建數(shù)組對象之后,就可以存儲數(shù)據(jù)到數(shù)組元素空間中,進(jìn)行數(shù)組元素的分配,也就是為數(shù)組元素賦值。為數(shù)組元素賦值的方式有3種:靜態(tài)初始化、動態(tài)初始化、通過數(shù)組下標(biāo)為數(shù)組賦值。
1.靜態(tài)初始化
靜態(tài)初始化就是在聲明數(shù)組時,由開發(fā)者顯式指定每個數(shù)組元素的初始值,初始值的類型要和定義數(shù)組的類型一致。根據(jù)這些初始值,系統(tǒng)會自動創(chuàng)建對應(yīng)長度的數(shù)組空間,用于存儲每個數(shù)組元素的數(shù)據(jù)。靜態(tài)初始化語法如下:
數(shù)據(jù)類型[] 數(shù)組名 = {數(shù)據(jù)1,數(shù)據(jù)2,數(shù)據(jù)3,…,數(shù)據(jù)n}; // 靜態(tài)初始化,第1種方式
數(shù)據(jù)類型[] 數(shù)組名;
數(shù)組名 = new 數(shù)據(jù)類型[]{數(shù)據(jù)1,數(shù)據(jù)2,數(shù)據(jù)3,…,數(shù)據(jù)n}; // 靜態(tài)初始化,第2種方式
第1種靜態(tài)初始化的方式,需要在聲明數(shù)組的同時進(jìn)行數(shù)據(jù)初始化,初始化的數(shù)據(jù)要寫在大括號中,并以逗號分隔。第2種靜態(tài)初始化的方式可以先聲明數(shù)組變量,然后使用new運(yùn)算符進(jìn)行數(shù)組元素的初始化。另外,第2種方式中,右邊表達(dá)式中的“[ ]”不允許寫數(shù)組長度,否則會發(fā)生語法錯誤。
靜態(tài)初始化示例代碼如下:
int[] nums = {10,20,30,40,50}; // 聲明數(shù)組并進(jìn)行數(shù)組靜態(tài)初始化
String[] names; // 聲明數(shù)組變量
names = new String[]{"唐僧","孫悟空","豬八戒","沙和尚"}; // 進(jìn)行靜態(tài)初始化
2.動態(tài)初始化
進(jìn)行數(shù)組動態(tài)初始化時,開發(fā)者只需要指定數(shù)組長度,然后由系統(tǒng)自動為數(shù)組元素分配初始值。動態(tài)初始化的語法格式如下:
數(shù)據(jù)類型[] 數(shù)組名 = new 數(shù)據(jù)類型[數(shù)組長度];
在進(jìn)行動態(tài)初始化后,程序會根據(jù)指定的數(shù)組長度,創(chuàng)建對應(yīng)長度的數(shù)組元素空間,并為每個數(shù)組元素空間設(shè)置初始值。
動態(tài)初始化的示例代碼如下:
int[] nums = new int[5]; // 創(chuàng)建長度為5的整型數(shù)組,數(shù)組元素的初始值都是0
String[] names = new String[3]; // 創(chuàng)建長度為3的字符串?dāng)?shù)組,數(shù)組元素的初始值都為null
3.通過數(shù)組下標(biāo)為數(shù)組賦值
在數(shù)組創(chuàng)建之后,可以使用數(shù)組名結(jié)合數(shù)組下標(biāo)的方式,為數(shù)組空間中的每個元素賦值。使用數(shù)組下標(biāo)賦值的語法格式如下:
數(shù)據(jù)類型[] 數(shù)組名 = new 數(shù)據(jù)類型[數(shù)組長度];
數(shù)組名[下標(biāo)1] = 數(shù)值1;
數(shù)組名[下標(biāo)2] = 數(shù)值2;
…
數(shù)組名[數(shù)組長度-1] = 數(shù)值n;
在通過數(shù)組下標(biāo)為數(shù)組元素賦值的時候,數(shù)組下標(biāo)的取值范圍從0到數(shù)組長度減1為止。下標(biāo)超出這個范圍,會發(fā)生“ArrayIndexOutOfBoundsException”數(shù)組下標(biāo)越界的異常。
通過數(shù)組下標(biāo)為元素賦值的代碼示例如下:
String[] names = new String[4]; // 聲明長度為4的字符串?dāng)?shù)組
names[0] = "唐僧"; // 通過下標(biāo)為每個數(shù)組元素賦值
names[1] = "孫悟空";
names[2] = "豬八戒";
names[3] = "沙和尚";
4.1.3 數(shù)組元素的訪問
數(shù)組創(chuàng)建之后,最常用的操作就是訪問數(shù)組元素,這包含為數(shù)組元素賦值和輸出數(shù)組元素中的值。訪問數(shù)組元素的方式是通過數(shù)組名結(jié)合數(shù)組下標(biāo)的方式完成的。
接下來,通過實(shí)例來演示如何訪問數(shù)組元素,如例4-1所示。
例4-1 Demo0401.java
1 ?package com.aaa.p040103;
2
3 ?public class Demo0401 {
4 ? public static void main(String[] args) {
5 ? int[] nums = new int[3]; // 定義長度為3的整型數(shù)組
6 ? nums[0] = 10; // 為數(shù)組元素賦值
7 ? nums[1] = 20;
8 ? nums[2] = 30;
9 ? System.out.println(nums[0]); // 輸出數(shù)組元素中的值
10 ? System.out.println(nums[1]);
11 ? System.out.println(nums[2]);
12 ? }
13 ?}
程序運(yùn)行結(jié)果如下:
10
20
30
在例4-1中,先創(chuàng)建了一個長度為3的整型數(shù)組,然后使用數(shù)組名結(jié)合下標(biāo)的方式分別為3個數(shù)組元素賦值。注意,數(shù)組的下標(biāo)必須寫在中括號內(nèi)。最后,使用打印輸出語句輸出每個數(shù)組元素內(nèi)的數(shù)據(jù)。
4.1.4 length的使用
要獲取數(shù)組的長度,可以通過數(shù)組對象的length屬性得到。每個數(shù)組都會有l(wèi)ength屬性,當(dāng)通過length屬性獲取了數(shù)組長度之后,就可以通過循環(huán)的方式,使用下標(biāo)逐一遍歷數(shù)組中的每個元素。
接下來,我們使用length獲取數(shù)組長度,并通過循環(huán)遍歷數(shù)組元素,如例4-2所示。
例4-2 Demo0402.java
1 ?package com.aaa.p040104;
2
3 ?public class Demo0402 {
4 ? public static void main(String[] args) {
5 ? int[] nums = new int[3]; // 創(chuàng)建長度為3的數(shù)組對象
6 ? nums[0] = 10; // 為數(shù)組元素賦值
7 ? nums[1] = 20;
8 ? nums[2] = 30;
9 ? for(int i = 0;i < nums.length;i++) { // 使用length獲取數(shù)組長度,作為循環(huán)條件
10 ? System.out.println(nums[i]); // 循環(huán)輸出每個元素
11 ? }
12 ? }
13 ?}
程序運(yùn)行結(jié)果如下:
10
20
30
在例4-2中,先定義了長度為3的整型數(shù)組,然后使用數(shù)組名結(jié)合下標(biāo)的方式為每個數(shù)組元素賦值,接著使用數(shù)組的length屬性獲取數(shù)組的長度,作為for循環(huán)的循環(huán)條件,最后通過for循環(huán)逐一遍歷數(shù)組元素,并打印輸出。
4.1.5 使用foreach遍歷數(shù)組
除了使用for循環(huán)遍歷數(shù)組外,Java中還有另外一種很簡潔的遍歷方法:foreach循環(huán)遍歷。這種方式也稱為增強(qiáng)for循環(huán),它的功能比較強(qiáng)大,遍歷時不需要依賴數(shù)組的長度和下標(biāo),即可實(shí)現(xiàn)數(shù)組遍歷。foreach循環(huán)的語法格式如下:
for (數(shù)組中元素類型 臨時變量 : 數(shù)組對象變量){
程序語句;
}
通過上面的語法結(jié)構(gòu)可以看出,foreach遍歷數(shù)組的時候不需要獲取數(shù)組長度,也不需要用索引去訪問數(shù)組中的元素,這是與for循環(huán)不同的地方。foreach循環(huán)會自動將數(shù)組中的元素逐一取出,存入一個臨時變量中,然后使用臨時變量進(jìn)行數(shù)據(jù)處理,從而完成數(shù)組元素的遍歷。
接下來,通過案例來演示foreach循環(huán)遍歷數(shù)組,如例4-3所示。
例4-3 Demo0403.java
1 ?package com.aaa.p040105;
2
3 ?public class Demo0403 {
4 ? public static void main(String[] args) {
5 ? String[] names = {"唐僧","孫悟空","豬八戒","沙和尚"}; // 聲明數(shù)組并進(jìn)行初始化
6 ? for(String name : names) { // 使用foreach循環(huán)逐一取出數(shù)組元素并存入臨時變量
7 ? System.out.println(name); // 輸出臨時變量存儲的數(shù)據(jù)
8 ? }
9 ? }
10 ?}
程序運(yùn)行結(jié)果如下:
唐僧
孫悟空
豬八戒
沙和尚
例4-3中,首先定義了一個數(shù)組對象,并初始化了4個字符串?dāng)?shù)據(jù)。然后使用foreach循環(huán)遍歷數(shù)組,每次循環(huán)時foreach都通過臨時變量存儲當(dāng)前遍歷到的元素,并將元素打印顯示。
注意:foreach循環(huán)代碼簡潔,編寫方便,但是有其局限性,當(dāng)使用foreach遍歷數(shù)組時,只能訪問其中的元素,不能對元素進(jìn)行修改。
接下來,通過案例進(jìn)一步演示在使用foreach循環(huán)的過程中,對元素進(jìn)行修改會有什么結(jié)果,如例4-4所示。
例4-4 Demo0404.java
1 ?package com.aaa.p040105;
1
1 ?public class Demo0404 {
1 ? public static void main(String[] args) {
1 ? String[] strs = new String[3]; // 創(chuàng)建一個長度為3的數(shù)組
1 ? int i = 0;
1 ? for(String str : strs) { // 循環(huán)遍歷數(shù)組
1 ? str = new String("字符串:" + i ); // 修改每個遍歷到的值
1 ? i++;
1 ? }
1 ? for(String str : strs) {
1 ? System.out.println(str); // 打印數(shù)組中的值
1 ? }
1 ? }
1 ?}
程序運(yùn)行結(jié)果如下:
null
null
null
例4-4中,先定義一個長度為3的字符串?dāng)?shù)組。然后通過第1個foreach循環(huán),將遍歷到的每個數(shù)組元素的數(shù)據(jù)都進(jìn)行了修改。但在第2個foreach循環(huán)中,遍歷輸出的每個元素依舊是null。這說明在使用foreach循環(huán)遍歷時,遍歷的元素并沒有真正被修改。原因是第8行中只是將臨時變量str指向了一個新字符串,變量str和數(shù)組中的元素實(shí)際上沒有任何聯(lián)系。所以,foreach循環(huán)的過程中無法修改所遍歷的數(shù)據(jù)。因此,foreach并不能替代for循環(huán),僅僅是讓遍歷的方法變得更簡潔。
4.1.6 基本類型數(shù)組的初始化
按照數(shù)據(jù)類型的不同,數(shù)組可分為基本類型數(shù)組和引用類型數(shù)組。基本類型數(shù)組的特點(diǎn)是,數(shù)組元素的值是直接存儲在數(shù)組元素中的。所以,定義基本類型數(shù)組并初始化時,會先為數(shù)組分配空間,然后將數(shù)據(jù)直接存入對應(yīng)的數(shù)組元素中。基本類型數(shù)組的初始化示例如圖4.2所示。
(a)定義值類型數(shù)組并由系統(tǒng)自動初始化 (b)為值類型數(shù)組設(shè)置數(shù)據(jù)
圖4.2 值類型數(shù)組的初始化和設(shè)值
在圖4.2中,左圖定義了一個值類型數(shù)組,也就是整型數(shù)組nums。該數(shù)組長度為5,初始化后每個數(shù)組元素的值都是0。右圖是為nums數(shù)組中的元素都設(shè)置一個整數(shù)值。從圖4.2中可以看出,值類型數(shù)組的數(shù)據(jù)都是直接存儲在數(shù)組元素中的。
接下來,通過案例來演示值類型數(shù)組的初始化和設(shè)值,如例4-5所示。
例4-5 Demo0405.java
1 ?package com.aaa.p040106;
2
3 ?public class Demo0405 {
4 ? public static void main(String[] args) {
5 ? int[] nums = new int[5]; // 定義長度為5的數(shù)組,并動態(tài)初始化
6 ? for(int n : nums) { // 使用foreach輸出每個元素值
7 ? System.out.println(n);
8 ? }
9
10 ? System.out.println("=========="); // 輸出分隔符
11
12 ? nums[0] = 34; // 為每個數(shù)組元素設(shè)置特定值
13 ? nums[1] = 21;
14 ? nums[2] = 15;
15 ? nums[3] = 56;
16 ? nums[4] = 71;
17
18 ? for(int i = 0;i < nums.length;i++) { // 使用for循環(huán)輸出每個元素
19 ? System.out.println(nums[i]);
20 ? }
21 ? }
22 ?}
程序運(yùn)行結(jié)果如下:
0
0
0
0
0
==========
34
21
15
56
71
在例4-5中,先定義了一個長度為5的整型數(shù)組。接著,使用foreach循環(huán)輸出數(shù)組元素中的數(shù)據(jù),此時數(shù)組中的數(shù)據(jù)都為0。然后,通過數(shù)組名結(jié)合下標(biāo)的方式,為每個數(shù)組元素設(shè)置特定數(shù)據(jù),最后使用for循環(huán)將數(shù)組元素的數(shù)據(jù)逐一打印出來。
4.1.7 引用類型數(shù)組的初始化
引用類型數(shù)組中存儲的是數(shù)據(jù)的引用地址。通過引用地址指向了實(shí)際存儲數(shù)據(jù)的內(nèi)存區(qū)域。下面通過定義一個Student類型的數(shù)組來演示引用類型數(shù)組的使用,如例4-6所示。
例4-6 Demo0406.java
1 ?package com.aaa.p040107;
2
3 ?class Student{ // 學(xué)生類
4 ? String name; // 姓名
5 ? int age; // 年齡
6 ?}
7
8 ?public class Demo0406 {
9 ? public static void main(String[] args) {
10 ? Student[] stus = new Student[2]; // 創(chuàng)建長度為2的學(xué)生數(shù)組
11 ? stus[0] = new Student(); // 為第一個數(shù)組元素存儲學(xué)生對象
12 ? stus[0].name = "張三"; // 設(shè)置學(xué)生對象的屬性
13 ? stus[0].age = 20;
14
15 ? stus[1] = new Student(); // 為第二個數(shù)組元素存儲學(xué)生對象
16 ? stus[1].name = "李四"; // 設(shè)置學(xué)生對象的屬性
17 ? stus[1].age = 18;
18
19 ? for(Student s : stus) { // 使用foreach循環(huán)輸出學(xué)生對象數(shù)據(jù)
20 ? System.out.println(s.name + " " + s.age);
21 ? }
22 ? }
23 ?}
程序運(yùn)行結(jié)果如下:
張三 20
李四 18
在例4-6中,先定義了一個長度為2的Student類型的數(shù)組。接著,為每個數(shù)組元素存儲一個學(xué)生類型的對象,并為存儲的學(xué)生對象設(shè)置屬性。然后,使用foreach循環(huán)輸出數(shù)組元素中的學(xué)生對象,將學(xué)生的姓名和年齡打印出來。
為了便于大家更好地理解引用類型數(shù)組存儲數(shù)據(jù)的特點(diǎn)。下面通過一個圖例對引用類型數(shù)組的使用進(jìn)行說明,如圖4.3所示。
(a)引用類型數(shù)組的初始化 (b)引用類型數(shù)組存儲數(shù)據(jù)
圖4.3 引用類型數(shù)組的數(shù)據(jù)存儲方式
圖4.3中,左圖是引用類型數(shù)組定義并初始化的情況,引用類型數(shù)組的元素默認(rèn)值都為null,不指向任何數(shù)據(jù)。右圖中,定義了學(xué)生類型的數(shù)組,數(shù)組長度為2,數(shù)組包含兩個元素。每個數(shù)組元素都存儲了一個地址,分別指向不同的學(xué)生對象。
4.2 二維數(shù)組
一維數(shù)組主要用于存儲線性數(shù)據(jù),比如存儲某校某年級所有學(xué)生一門課程的成績,這種數(shù)據(jù)存儲的結(jié)構(gòu)是單個維度的。但是在實(shí)際應(yīng)用中,一維數(shù)組并不能滿足所有需求,比如存儲某校某年級所有學(xué)生兩門課程的成績。所以,Java中提供了多維數(shù)組,但是Java中并沒有真正的多維數(shù)組結(jié)構(gòu),它的多維數(shù)組的本質(zhì)是讓數(shù)組元素再存儲一個數(shù)組,從而構(gòu)成多維數(shù)組的結(jié)構(gòu)。本節(jié)將以二維數(shù)組為例講解多維數(shù)組的用法。二維數(shù)組的數(shù)據(jù)結(jié)構(gòu)類似于一張表,包含行和列,下面展開詳細(xì)講解。
4.2.1 二維數(shù)組的創(chuàng)建
二維數(shù)組的結(jié)構(gòu)可以看作是一張表,其中包含行和列,分別對應(yīng)第一維度和第二維度。如圖4.4所示,是一個3行2列的二維數(shù)組的存儲結(jié)構(gòu)示意圖。
圖4.4 3行2列二維數(shù)組結(jié)構(gòu)
在Java中,聲明二維數(shù)組時,需要使用兩個中括號進(jìn)行定義,在分配二維數(shù)組元素空間時,需要指明兩個維度的數(shù)組長度。二維數(shù)組的創(chuàng)建和一維數(shù)組類似,也需要進(jìn)行數(shù)組聲明和數(shù)組空間分配,創(chuàng)建二維數(shù)組的語法如下:
數(shù)據(jù)類型[][] 數(shù)組名; // 聲明二維數(shù)組變量
數(shù)組名 = new 數(shù)據(jù)類型[第一維長度][第二維長度]; // 為二維數(shù)組分配空間
當(dāng)然也可以將數(shù)組聲明和數(shù)組的內(nèi)存分配用一行代碼定義:
數(shù)據(jù)類型[][] 數(shù)組名 = new 數(shù)據(jù)類型[第一維長度][第二維長度]; // 聲明二維數(shù)組并分配空間
根據(jù)二維數(shù)組的語法,創(chuàng)建一個3行2列的整型二維數(shù)組的示例代碼如下:
int[][] scores; // 聲明二維數(shù)組變量scores
scores = new int[3][2]; // 為二維數(shù)組分配3行2列的內(nèi)存空間
在上述示例代碼中,第1行聲明了一個整型二維數(shù)組變量scores,這個變量未指向任何數(shù)組空間。接著,第2行代碼先通過new運(yùn)算符創(chuàng)建了一個3行2列的整型二維數(shù)組空間。然后,將二維數(shù)組賦值給scores變量,因此 scores變量存儲了整型二維數(shù)組的地址。通過scores變量可以訪問二維數(shù)組對象空間的每個元素。對于二維數(shù)組的聲明和內(nèi)存分配,如圖4.5所示。
(a)聲明整型二維數(shù)組變量但未指向數(shù)組對象空間 (b)聲明整型二維數(shù)組變量并指向二維數(shù)組對象空間
圖4.5 二維數(shù)組聲明和內(nèi)存分配
圖4.4中,左圖聲明了一個整型二維數(shù)組變量scores,因?yàn)闆]有指向任何數(shù)組對象,所以scores變量存儲的內(nèi)容是null。右圖在堆內(nèi)存中創(chuàng)建了一個3行2列的整型二維數(shù)組。右圖的scores變量存儲了這個二維數(shù)組的首地址。二維數(shù)組的第一維是一個長度為3的一維數(shù)組,這個一維數(shù)組的每個空間都指向到另外一個長度為2的一維數(shù)組,由此構(gòu)成了二維數(shù)組結(jié)構(gòu)。通過scores變量使用兩個維度的下標(biāo)可以訪問到二維數(shù)組中的每個數(shù)組元素。
4.2.2 二維數(shù)組的內(nèi)存分配
二維數(shù)組創(chuàng)建之后,就可以存儲數(shù)據(jù)到二維數(shù)組的元素空間中。二維數(shù)組元素的賦值方式與一維數(shù)組類似,也是3種方式:靜態(tài)初始化、動態(tài)初始化、通過數(shù)組下標(biāo)為數(shù)組賦值。
1.靜態(tài)初始化
二維數(shù)組的靜態(tài)初始化就是在聲明數(shù)組時,由開發(fā)者顯示指定二維數(shù)組元素的初始值。因?yàn)槎S數(shù)組有兩個維度,所以在設(shè)置數(shù)據(jù)的時候,要使用兩層大括號來體現(xiàn)兩個維度的結(jié)構(gòu)。如下是兩種二維數(shù)組靜態(tài)初始化的語法:
// 聲明數(shù)組同時初始化,外面的大括號表示第一維元素,里面的大括號表示第二維元素
數(shù)據(jù)類型[][] 數(shù)組名 = {{數(shù)據(jù)1,數(shù)據(jù)2,…},{數(shù)據(jù)1,數(shù)據(jù)2,…},…};
// 先聲明二維數(shù)組,然后進(jìn)行初始化
數(shù)據(jù)類型[][] 數(shù)組名 ;
數(shù)組名 = new 數(shù)據(jù)類型[][]{{數(shù)據(jù)1,數(shù)據(jù)2,…},{數(shù)據(jù)1,數(shù)據(jù)2,…},…};
上述語法中,二維數(shù)組靜態(tài)初始化時,初始化的數(shù)據(jù)要寫在兩層大括號中,這是與一維數(shù)組不同的地方。第1層大括號中定義的是第一維的數(shù)組元素,這些數(shù)組元素本身又是一個數(shù)組,元素之間以逗號分隔。第2層大括號中存儲的是實(shí)際的數(shù)據(jù)內(nèi)容,多個數(shù)據(jù)之間也以逗號分隔。另外,在第2種靜態(tài)初始的語法中,右邊表達(dá)式中的“[ ][ ]”內(nèi),也不允許寫數(shù)組長度,否則會發(fā)生語法錯誤。
二維數(shù)組靜態(tài)初始化示例代碼如下:
int[][] scores = {{78,65},{98,79},{87,89}}; // 聲明數(shù)組并進(jìn)行數(shù)組靜態(tài)初始化
int[][] scores; // 先聲明數(shù)組變量
scores = new int[][]{{78,65},{98,79},{87,89}}; // 再進(jìn)行靜態(tài)初始化
2.動態(tài)初始化
二維數(shù)組進(jìn)行動態(tài)初始化時,開發(fā)者需要指定兩個維度的數(shù)組長度,然后由系統(tǒng)自動為數(shù)組元素分配初始值。二維數(shù)組動態(tài)初始化的語法格式如下:
數(shù)據(jù)類型[][] 數(shù)組名 = new 數(shù)據(jù)類型[第一維數(shù)組長度][第二維數(shù)組長度];
二維數(shù)組在進(jìn)行動態(tài)初始化后,程序會根據(jù)指定的兩個維度的數(shù)組長度,創(chuàng)建對應(yīng)的數(shù)組元素空間,并為每個數(shù)組元素空間設(shè)置初始值。
二維數(shù)組動態(tài)初始化的代碼示例如下:
int[][] scores = new int[3][2]; // 創(chuàng)建3行2列的整形二維數(shù)組,元素的初始值都是0
String[][] names = new String[3][2];// 創(chuàng)建3行2列的字符串二維數(shù)組,元素的初始值都為null
3.通過數(shù)組下標(biāo)為數(shù)組賦值
在二維數(shù)組創(chuàng)建之后,可以使用數(shù)組名結(jié)合二維數(shù)組行列下標(biāo)的方式,為二維數(shù)組空間中的每個元素賦值。使用二維數(shù)組下標(biāo)賦值的語法格式如下:
數(shù)據(jù)類型[][] 數(shù)組名 = new 數(shù)據(jù)類型[第一維數(shù)組長度][第二維數(shù)組長度];
數(shù)組名[第一維度下標(biāo)][第二維度下標(biāo)] = 數(shù)值;
在通過數(shù)組下標(biāo)為數(shù)組元素賦值的時候,每個維度的數(shù)組下標(biāo)的取值范圍從0到對應(yīng)維度的數(shù)組長度減1為止。下標(biāo)超出這個范圍,會發(fā)生“ArrayIndexOutOfBoundsException”數(shù)組下標(biāo)越界的異常。
通過數(shù)組下標(biāo)為元素賦值的示例代碼如下:
String[][] names = new String[3][2]; // 聲明3行2列字符串二維數(shù)組
names[0][0] = "張三"; // 通過2個維度的下標(biāo)為每個數(shù)組元素賦值
names[0][1] = "李四";
names[1][0] = "王五";
names[1][1] = "趙六";
names[2][0] = "孫七";
names[2][1] = "錢八";
4.2.3 嵌套循環(huán)存取二維數(shù)組
二維數(shù)組有兩個維度,在訪問數(shù)組元素的時候要通過兩個下標(biāo)來訪問。因?yàn)閿?shù)組的下標(biāo)具有連續(xù)性的特點(diǎn),所以可以通過循環(huán)嵌套的方式來訪問二維數(shù)組的每個元素。
接下來,通過循環(huán)嵌套的方式為一個3行2列的二維數(shù)組賦值,并用循環(huán)嵌套的方式輸出存入的數(shù)據(jù),如例4-7所示。
例4-7 Demo0407.java
1 ?package com.aaa.p040203;
2 ?import java.util.Scanner;
3
4 ?public class Demo0407 {
5 ? public static void main(String[] args) {
6 ? Scanner sc = new Scanner(System.in); // 定義輸入掃描器對象,用于接受鍵盤輸入
7 ? // 定義3行2列的二維數(shù)組,存儲3名學(xué)生的2門成績
8 ? int[][] scores = new int[3][2];
9 ? // 使用嵌套循環(huán)的方式為數(shù)組賦值, scores.length獲取第一維數(shù)組的長度
10 ? for(int i = 0; i < scores.length; i++) {
11 ? // scores[i].length獲取第二維數(shù)組的長度
12 ? for(int j = 0; j < scores[i].length; j++) {
13 ? System.out.println("請輸入第" + ( i + 1 ) + "個學(xué)生的第" + ( j + 1 ) +
14 ? "門課成績:");
15 ? scores[i][j] = sc.nextInt(); // 將輸入的數(shù)據(jù)存入對應(yīng)的數(shù)組位置
16 ? }
17 ? }
18
19 ? System.out.println("===================="); // 輸出分隔符
20
21 ? // 使用嵌套循環(huán)的方式打印二維數(shù)組元素, scores.length獲取第一維數(shù)組的長度
22 ? for(int i = 0; i < scores.length; i++) {
23 ? // scores[i].length獲取第二維數(shù)組的長度
24 ? for(int j = 0; j < scores[i].length; j++) {
25 ? int score = scores[i][j]; // 獲取當(dāng)前遍歷到的二維數(shù)組元素
26 ? // 輸出分?jǐn)?shù)
27 ? System.out.println("第" + (i + 1) + "個學(xué)生的第" + (j + 1) +
28 ? "門課成績是:" + score);
29 ? }
30 ? }
31
32 ? }
33 ?}
程序運(yùn)行結(jié)果如下:
請輸入第1個學(xué)生的第1門課成績:
23
請輸入第1個學(xué)生的第2門課成績:
34
請輸入第2個學(xué)生的第1門課成績:
45
請輸入第2個學(xué)生的第2門課成績:
56
請輸入第3個學(xué)生的第1門課成績:
67
請輸入第3個學(xué)生的第2門課成績:
87
====================
第1個學(xué)生的第1門課成績是:23
第1個學(xué)生的第2門課成績是:34
第2個學(xué)生的第1門課成績是:45
第2個學(xué)生的第2門課成績是:56
第3個學(xué)生的第1門課成績是:67
第3個學(xué)生的第2門課成績是:87
在例4-7中,先定義了一個輸入掃描器對象(Scanner),用于接受從鍵盤輸入的數(shù)據(jù)。接著,定義了一個3行2列的整型二維數(shù)組,用于存儲3個學(xué)生兩門課程的成績。然后使用嵌套循環(huán)實(shí)現(xiàn)二維數(shù)組元素的賦值。scores.length是第1層循環(huán)的終止條件,它表示的是第一維度數(shù)組的長度。scores[i].length是第二層循環(huán)的終止條件,它表示的是第二維度數(shù)組的長度。在第2層循環(huán)內(nèi)部,使用scores[i][j]表示訪問到的某個二維數(shù)組元素,用于存儲用戶通過鍵盤輸入的數(shù)據(jù)。嵌套循環(huán)結(jié)束后,這個二維數(shù)組的所有元素都會被賦值。接下來通過另外一個嵌套循環(huán),將二維數(shù)組內(nèi)的所有元素逐一打印輸出。
知識點(diǎn)撥:Java中的多維數(shù)組本質(zhì)上是在數(shù)組中存儲數(shù)組,是在一維數(shù)組的基礎(chǔ)上衍生出來的,因此理論上可以定義任何維度的數(shù)組。定義二維數(shù)組的時候需要使用兩個中括號"[ ][ ]",以此類推,定義三維、四維數(shù)組只要定義對應(yīng)個數(shù)的中括號即可。
4.2.4 非對稱型數(shù)組
前面講的二維數(shù)組用的都是矩陣數(shù)組,也就是數(shù)組的第二維度的長度都是一樣的,是等行等列的對稱結(jié)構(gòu)。但是,Java中還有一種非對稱的數(shù)組結(jié)構(gòu),被稱為非對稱型數(shù)組。對稱型數(shù)組和非對稱型數(shù)組的結(jié)構(gòu)如圖4.6所示。
(a)對稱型數(shù)組(矩形數(shù)組) (b)非對稱型數(shù)組(不規(guī)則數(shù)組)
圖4.6 對稱型數(shù)組和非對稱型數(shù)組
對稱型的3行4列數(shù)組有12個元素,但是非對稱型數(shù)組的元素個數(shù)是不確定的。這里,我們定義一個非對稱型數(shù)組,并進(jìn)行靜態(tài)初始化,然后輸出一維數(shù)組元素指向的數(shù)組中的最后一個元素,示例代碼如下:
int[][] scores = {{1,2,3},{5,6,7,8},{9,10}}; // 用靜態(tài)初始化的方式定義非對稱型數(shù)組
System.out.println(scores[0][2]); // 輸出第一維下標(biāo)為0的數(shù)組的最后一個元素
System.out.println(scores[1][3]); // 輸出第一維下標(biāo)為1的數(shù)組的最后一個元素
System.out.println(scores[2][1]); // 輸出第一維下標(biāo)為2的數(shù)組的最后一個元素
在上述示例代碼中,聲明的二維數(shù)組,第一維長度是3。但是,第二維長度各不相同,長度分別是:3,4,2。因此,第一維數(shù)組元素關(guān)聯(lián)的數(shù)組的最后一個元素的下標(biāo)是各不相同的。
4.3 數(shù)組的排序與查找
使用數(shù)組處理數(shù)據(jù)的時候,除了要對數(shù)據(jù)進(jìn)行存儲,大部分時候需要對數(shù)據(jù)進(jìn)行查找和篩選,而查找數(shù)據(jù)的時候又往往需要對數(shù)據(jù)進(jìn)行排序處理。比如,要找一個分?jǐn)?shù)最高的成績,或者年齡最小的學(xué)生,這時就需要在查找的過程中進(jìn)行數(shù)據(jù)排序。
4.3.1 數(shù)組元素排序
對數(shù)組中的數(shù)據(jù)進(jìn)行排序的算法有很多種,對于初學(xué)者而言,“冒泡排序”是最簡單易懂的方法。冒泡排序的核心特點(diǎn),就是先比較相鄰兩個元素的大小,然后根據(jù)排序規(guī)則進(jìn)行換位。
如圖4.7所示,是對一維數(shù)組中的5個數(shù)字:56,32,8,76,12進(jìn)行冒泡排序,要求排序之后,數(shù)組中的數(shù)據(jù)按照從小到大的次序排列。
圖4.7 冒泡排序
圖4.7中,在開始排序時,5個數(shù)字沒有按從小到大的次序進(jìn)行排列。接著開始進(jìn)行第1輪排序,排序的時候依次對相鄰的兩個數(shù)字比較大小,如果前面的數(shù)字大于后面的數(shù)字,則將這兩個數(shù)字交換位置,否則就不進(jìn)行換位。參與排序的5個數(shù)字全都比較一遍并做了相應(yīng)的換位之后,數(shù)值最大的76排在了最后的位置上,這個數(shù)字不再參與下一輪的比較。第1輪排序一共進(jìn)行了4次比較和換位。在第2輪排序時,排除了上一輪的最大值76后,只需要對4個數(shù)字進(jìn)行排序。排序的過程依舊是比較和換位。最終經(jīng)過3次比較,找到了本輪的最大值56。第3輪排序時,將前兩輪的最大值72和56排除掉,只需要對剩下的3個數(shù)字進(jìn)行排序。最終經(jīng)過2次比較和換位,找到了本輪的最大值32。第4輪排序時,將前3輪的最大值72、56、32排除掉,只需要對剩下的2個數(shù)字進(jìn)行排序,最終經(jīng)過1次比較找到了本輪的最大值12。經(jīng)過4輪排序,5個數(shù)字實(shí)現(xiàn)了從小到大的排列。
經(jīng)過上面的排序過程可以發(fā)現(xiàn),排序的輪數(shù)等于參與排序的數(shù)字的個數(shù)減1。而每一輪排序的次數(shù)等于本輪參與排序的數(shù)字個數(shù)減1。這是因?yàn)槊恳惠喤判蚨紩业奖据喌淖畲笾担谙乱惠喤判驎r這個最大值不再參與比較,所以隨著排序輪數(shù)的增加,參與比較的數(shù)字在減少,這是冒泡排序的一個基本規(guī)律。
在了解了冒泡排序的基本原理后,下面用代碼實(shí)現(xiàn)冒泡排序的過程,如例4-8所示:
例4-8 Demo0408.java
1 ?package com.aaa.p040301;
2
3 ?public class Demo0408 {
4 ? public static void main(String[] args) {
5 ? int[] nums = {56, 32, 8, 76, 12}; // 定義要排序的整形數(shù)組
6 ? System.out.println("=========排序前===========");
7 ? for(int n : nums) { // 循環(huán)輸出初始的數(shù)組數(shù)據(jù)
8 ? System.out.print(n+" ");
9 ? }
10 ? System.out.println(); // 換行
11 ? // 使用雙重循環(huán)實(shí)現(xiàn)冒泡排序,外層循環(huán)控制排序比較的輪數(shù)
12 ? for(int i = 1;i < nums.length; i++) {
13 ? for(int j = 0;j < nums.length - i;j++) { // 內(nèi)層循環(huán)控制每一輪比較的次數(shù)
14 ? if(nums[j] > nums[j + 1]) { // 每次比較相鄰的兩個元素的大小
15 ? // 若前元素大于后元素,則交換位置,先將前元素的數(shù)據(jù)存入臨時變量
16 ? int temp = nums[j];
17 ? nums[j] = nums[j + 1]; // 將后元素的數(shù)據(jù)存到前元素中
18 ? nums[j + 1] = temp; // 將臨時變量數(shù)據(jù)存儲到后元素中
19 ? }
20 ? }
21 ? }
22 ? System.out.println("=========排序后===========");
23 ? for(int n : nums) { // 循環(huán)輸出排序后的數(shù)組數(shù)據(jù)
24 ? System.out.print(n + " ");
25 ? }
26 ? }
27 ?}
程序運(yùn)行結(jié)果如下:
=========排序前===========
56 32 8 76 12
=========排序后===========
8 12 32 56 76
在例4-8中,先定義了一個整型一維數(shù)組,存儲了要排序的5個數(shù)字。接著使用循環(huán)將排序前的這5個數(shù)字打印輸出,用于和排序后的結(jié)果做對比。然后,使用嵌套的for循環(huán)實(shí)現(xiàn)冒泡排序的算法過程。在嵌套for循環(huán)中,外層循環(huán)用于控制排序比較的輪數(shù)。內(nèi)層循環(huán)用于控制每輪排序比較的次數(shù)。在內(nèi)層循環(huán)中,每次做比較時,如果參與比較的兩個元素,前面的元素比后面的元素大,則要進(jìn)行換位。換位的時候,先定義一個臨時變量,用于存儲參與比較的前面的數(shù)組元素,接著把后面的元素存入前面的元素中,最后再把臨時變量存儲的數(shù)據(jù)存入后面數(shù)組元素中,從而完成元素?fù)Q位。嵌套循環(huán)執(zhí)行完成之后,數(shù)組元素就完成了排序過程。最后,使用循環(huán)輸出排序后的數(shù)組數(shù)據(jù)。
編程技巧:對于初學(xué)者而言,冒泡排序的算法代碼不太容易掌握。大家只要記住下面四句話,那么冒泡排序就很容易寫出來:N元數(shù)組冒泡序,兩兩相比小前移。外層循環(huán)從1始,內(nèi)層循環(huán)減i去。
4.3.2 數(shù)組元素的查找
通過排序可以將數(shù)組元素排出大小順序,排序的目的是為了更有效地查找數(shù)據(jù)。而對于數(shù)據(jù)的查找,也有很多算法,本節(jié)主要講解順序查找和二分查找。
1.順序查找
順序查找法是最簡單的查找方法。可以對數(shù)組中的元素按照下標(biāo)依次對比,直到查找到目標(biāo)元素或者將所有數(shù)組元素遍歷完畢為止。順序查找法的效率較低,比如在N個元素中查找目標(biāo)數(shù)據(jù),平均要查找N/2次。所以,順序查找法一般用于對少量數(shù)據(jù)進(jìn)行查找,或者對未排序數(shù)據(jù)進(jìn)行查找。
接下來,演示從有10個元素的整型數(shù)組中,使用順序查找法查找數(shù)字13,找到則輸出元素在數(shù)組中的下標(biāo),找不到則進(jìn)行提示,如例4-9所示。
例4-9 Demo0409.java
1 ?package com.aaa.p040302;
2
3 ?public class Demo0409 {
4 ? public static void main(String[] args) {
5 ? // 定義長度為10的整形數(shù)組,并初始化
6 ? int[] nums = {34, 32, 45, 67, 98, 43, 31, 47, 13, 22};
7 ? int searchNum = 13; // 定義要查找的目標(biāo)數(shù)據(jù)
8 ? int index = -1; // 定義變量記錄查找到的目標(biāo)數(shù)據(jù)位置
9 ? for(int i = 0; i < nums.length; i++) { // 循環(huán)遍歷數(shù)組,用順序查找法查找目標(biāo)數(shù)據(jù)
10 ? if(nums[i] == searchNum) { // 判斷遍歷的當(dāng)前元素和目標(biāo)數(shù)據(jù)是否相等
11 ? index = i; // 如果相等則記錄目標(biāo)數(shù)據(jù)在數(shù)組中的位置
12 ? break; // 結(jié)束循環(huán)
13 ? }
14 ? }
15 ? if(index == -1) { // 循環(huán)結(jié)束后,判斷記錄目標(biāo)數(shù)據(jù)位置是否為-1
16 ? // 如果記錄的位置為-1,說明沒有找到數(shù)據(jù)
17 ? System.out.println("在數(shù)組中沒有要找到的目標(biāo)數(shù)據(jù)");
18 ? }else {
19 ? // 如果記錄的位置不為-1,則說明找到了目標(biāo)數(shù)據(jù)
20 ? System.out.println("找到了目標(biāo)數(shù)據(jù),位置是:" + index);
21 ? }
22
23 ? }
24 ?}
程序運(yùn)行結(jié)果如下:
找到了目標(biāo)數(shù)據(jù),位置是:8
在例4-9中,先定義了一個整型一維數(shù)組,存儲了10個數(shù)字。接著,定義了一個變量存儲要查找的目標(biāo)數(shù)據(jù)13。然后,定義index變量用于記錄查找結(jié)果,index變量的初始值設(shè)為-1。接著使用循環(huán)逐一獲取數(shù)組中的元素與目標(biāo)數(shù)據(jù)進(jìn)行比較。如果數(shù)組元素與目標(biāo)數(shù)據(jù)相等,就說明在數(shù)組中找到了目標(biāo)數(shù)據(jù),則用index存儲當(dāng)前數(shù)組元素的下標(biāo)位置,然后退出循環(huán)。在循環(huán)結(jié)束后判斷index的值,如果index值不是-1,則說明在數(shù)組元素中找到了目標(biāo)數(shù)據(jù),index存儲了目標(biāo)數(shù)據(jù)在數(shù)組中的位置,則將該位置打印輸出。否則,說明數(shù)組中不存在查找的目標(biāo)值,則打印輸出沒有找到的提示。
2.二分查找
二分查找法是查找效率比較高的查找算法,該方法的核心是在數(shù)組數(shù)據(jù)有序的基礎(chǔ)上,在數(shù)組中標(biāo)記低位和高位以限定查找范圍,并用查找范圍內(nèi)的中間位置的數(shù)據(jù)和目標(biāo)數(shù)據(jù)進(jìn)行比較,不斷調(diào)整低位和高位的索引位置從而縮小查找范圍,最終找到目標(biāo)值。如果在N個數(shù)據(jù)的范圍內(nèi)進(jìn)行二分查找,則平均會執(zhí)行l(wèi)og2N+1次查找比較。
如圖4.8所示,是在長度為10的整型數(shù)組中,使用二分查找法,查找數(shù)據(jù)47的過程。
圖4.8 二分查找法
圖4.8中,初始定義了長度為10的整型數(shù)組,并存儲了10個整數(shù)。然后,對數(shù)組進(jìn)行排序。接著,按照二分查找法的算法規(guī)則,在數(shù)組中標(biāo)記了低位(low)和高位(high)的索引位置,低位的索引位置初始為0,高位的索引位置初始為9(數(shù)組長度減1)。然后,通過低位和高位索引位置計(jì)算出一個中間位置,計(jì)算方法是:中間位置=(低位+高位)/2。根據(jù)這個公式計(jì)算得出中間位置的索引是4,此時使用該位置上的數(shù)據(jù)34和目標(biāo)數(shù)據(jù)47做對比,目標(biāo)數(shù)據(jù)47大于34,那么根據(jù)二分查找法的規(guī)則,為了縮小查找范圍,要將低位索引變?yōu)橹虚g索引加1,改變后低位索引的值就是5。接著使用改變后的低位索引和高位索引,重新計(jì)算得出一個新的中間位置(5+9)/2 = 7,此時中間位置7對應(yīng)的數(shù)據(jù)是47,這個就是查找的目標(biāo)數(shù)據(jù)。到此,二分查找法的整個過程結(jié)束。
接下來,使用代碼演示的二分查找法的具體過程,如例4-10所示。
例4-10 Demo0410.java
1 ?package com.aaa.p040302;
2
3 ?public class Demo0410 {
4 ? public static void main(String[] args) {
5 ? // 定義長度為10的整形數(shù)組,并初始化
6 ? int[] nums = {34, 32, 45, 67, 98, 43, 31, 47, 13, 22};
7 ? System.out.println("=========排序前===========");
8 ? for(int n : nums) { // 循環(huán)輸出排序前的數(shù)組數(shù)據(jù)
9 ? System.out.print(n+" ");
10 ? }
11 ? System.out.println(); // 輸出換行
12 ? // 使用雙重循環(huán)實(shí)現(xiàn)冒泡排序,外層循環(huán)控制排序比較的輪數(shù)
13 ? for(int i = 1;i < nums.length;i++) { // 內(nèi)層循環(huán)控制每一輪比較的次數(shù)
14 ? for(int j = 0; j < nums.length - i;j++) { // 每次比較相鄰的兩個元素的大小
15 ? if(nums[j] > nums[j + 1]) {
16 ? // 如果前元素大于后元素,則交換位置,先將前元素的數(shù)據(jù)存入臨時變量
17 ? int temp = nums[j];
18 ? nums[j] = nums[j + 1]; // 將后元素的數(shù)據(jù)存到前元素中
19 ? nums[j + 1] = temp; // 將臨時變量數(shù)據(jù)存儲到后元素中
20 ? }
21 ? }
22 ? }
23
24 ? System.out.println("=========排序后===========");
25 ? for(int n : nums) { // 循環(huán)輸出排序后的數(shù)組數(shù)據(jù)
26 ? System.out.print(n + " ");
27 ? }
28 ? System.out.println(); // 輸出換行
29 ? System.out.println("=========使用二分查找法===========");
30 ? // 使用二分法查找數(shù)據(jù)
31 ? int searchNum = 47; // 定義要查找的目標(biāo)數(shù)據(jù)
32 ? int index = -1; // 定義變量記錄查找到的目標(biāo)數(shù)據(jù)的位置
33 ? int low = 0; // 定義低位索引變量,初始為0
34 ? int high = nums.length - 1; // 定義高位索引變量,初始為數(shù)組長度減1
35 ? int middle = -1; // 定義中間位置變量,初始為-1
36 ? do { // 通過循環(huán)實(shí)現(xiàn)二分查找過程
37 ? middle = (low + high) / 2; // 計(jì)算中間位置
38 ? if(nums[middle] == searchNum) {// 使用中間位置對應(yīng)的數(shù)據(jù)和目標(biāo)數(shù)據(jù)比較
39 ? index = middle; // 如果兩個數(shù)據(jù)相等,則用index存儲中間位置
40 ? break; // 退出循環(huán)
41 ? }
42 ? // 如果中間數(shù)據(jù)大于目標(biāo)數(shù)據(jù),則將高位索引設(shè)置為中間位置減1
43 ? if(nums[middle] > searchNum) {
44 ? high = middle - 1;
45 ? }else {
46 ? low = middle + 1; // 否則,將低位索引設(shè)置為中間位置加1
47 ? }
48 ? }while(low <= high); // 循環(huán)條件是低位索引位置小于高位索引位置
49 ? // 輸出二分查找結(jié)果
50 ? if(index == -1) {
51 ? // 如果index記錄的位置是-1,說明沒有找到目標(biāo)值
52 ? System.out.println("在數(shù)組中沒有找到目標(biāo)值");
53 ? }else{
54 ? // 如果index記錄的位置不是-1,說明找到了目標(biāo)值
55 ? System.out.println("找到了目標(biāo)值:" + searchNum + "它的索引位置是:" + index);
56 ? }
57 ? }
58 ?}
程序運(yùn)行結(jié)果如下:
=========排序前===========
34 32 45 67 98 43 31 47 13 22
=========排序后===========
13 22 31 32 34 43 45 47 67 98
=========使用二分查找法===========
找到了目標(biāo)值:47 它的索引位置是:7
在例4-10中,先定義了長度為10的整形數(shù)組,并存儲了10個整數(shù)。接著,輸出了排序前的數(shù)組元素。然后,對數(shù)組進(jìn)行了排序,并輸出了排序后的數(shù)組元素。接著,定義了二分查找法需要的相關(guān)變量。其中,低位索引變量的初始值是0,高位索引變量的初始值是9(數(shù)組長度減1)。然后,使用do...while循環(huán)實(shí)現(xiàn)二分查找法的算法過程。在循環(huán)開始的時候,先通過低位索引和高位索引計(jì)算出中間位置。接著,使用中間位置上對應(yīng)的數(shù)據(jù)和目標(biāo)數(shù)據(jù)做對比,如果中間位置上的數(shù)據(jù)和目標(biāo)數(shù)據(jù)相等,那就記錄中間位置的索引值,查找到此結(jié)束。如果中間位置對應(yīng)的數(shù)據(jù)比目標(biāo)值大,那么根據(jù)二分查找法的規(guī)則,為了縮小查找范圍,要將高位索引設(shè)置為中間位置索引減1。如果中間位置對應(yīng)的數(shù)據(jù)比目標(biāo)值小,那么要將低位索引設(shè)置為中間位置索引加1。只要低位索引小于等于高位索引,這個循環(huán)過程就一直持續(xù),直到找到目標(biāo)數(shù)據(jù)為止。最后,在循環(huán)結(jié)束后,使用記錄目標(biāo)數(shù)據(jù)索引值的index變量進(jìn)行判斷,如果該變量的值是-1,則提示沒有找到目標(biāo)數(shù)據(jù),否則說明找到了目標(biāo)數(shù)據(jù),并將目標(biāo)值在數(shù)組中的索引打印出來。
4.4 本章小結(jié)
? ?Java中使用數(shù)組需要3個步驟:聲明數(shù)組、創(chuàng)建數(shù)組對象空間、為數(shù)組元素賦值。
? ?為數(shù)組元素賦值可以使用靜態(tài)初始化、動態(tài)初始化、通過數(shù)組下標(biāo)為數(shù)組元素賦值。
? ?訪問數(shù)組中的元素時,要使用數(shù)組名加下標(biāo)的方式訪問。
? ?通過數(shù)組的length屬性可以獲取數(shù)組的長度。
? ?除了使用for循環(huán)結(jié)合下標(biāo)的方式遍歷數(shù)組,還可以使用foreach方式遍歷數(shù)組。
? ?基本類型的數(shù)組,其數(shù)組元素空間存儲的都是數(shù)據(jù)本身。
? ?引用類型的數(shù)組,其數(shù)組元素空間存儲的是數(shù)據(jù)的地址。
? ?二維數(shù)組就是讓數(shù)組元素再存儲一個數(shù)組,二維數(shù)組有兩個維度對應(yīng)行和列。
? ?二維數(shù)組的內(nèi)存分配和一維數(shù)組類似,也有3種方式:靜態(tài)初始化、動態(tài)初始化、使用數(shù)組下標(biāo)賦值。
? ?對二維數(shù)組而言,非對稱型數(shù)組是指數(shù)組的第二維的長度各不相等。
? ?冒泡排序的算法核心是相鄰兩個元素進(jìn)行比較和換位
? ?對數(shù)組元素查找的常用方法有:順序查找法和二分查找法
4.5 理論測試與實(shí)踐練習(xí)
1.填空題
1.1 數(shù)組的元素通過 來訪問,訪問數(shù)組的長度用 。
1.2 JVM將數(shù)組的存儲在 (堆或棧)中。
1.3 數(shù)組下標(biāo)訪問超出索引范圍時拋出 異常。
1.4 數(shù)組創(chuàng)建后其大小 (能/不能)改變。
1.5 Java中數(shù)組的最小下標(biāo)是 。
2.選擇題
2.1 定義了一維int類型數(shù)組a[10]后,下面錯誤的引用是( )
A.a(chǎn)[0]=1; B.a(chǎn)[10] = 2; C.a(chǎn)[0] = 5*2; D.a(chǎn)[1] = a[2]*a[0];
2.2 下面二維數(shù)組初始化語句中 ,正確的是( )
A.float b[2][2]={0.1,0.2,0.3,0.4};
B.int a[][]={{1,2},{3,4}};
C.int a[2][] = {{1,2},{3,4}};
D.float a[2][2]={0};
2.3 數(shù)組的第3個元素表示為( )
A.a(chǎn)(3) B.a(chǎn)[3] C.a(chǎn)(2) D.a(chǎn)[2]
2.4 通過哪些方式能夠?yàn)閿?shù)組賦值( )
A.靜態(tài)初始化 B.動態(tài)初始化 C.使用數(shù)組下標(biāo)賦值 D.聲明數(shù)組變量
2.5 關(guān)于字符串類型的數(shù)組,說話正確的是( )
A.其數(shù)組的默認(rèn)值是’’ B.其數(shù)組的默認(rèn)值是null
C.?dāng)?shù)組的長度可以任意變化 D.可以存儲整形數(shù)值
3.思考題
3.1 請簡述什么時候?yàn)閿?shù)組分配內(nèi)存?
3.2 請簡述數(shù)組一旦被創(chuàng)建,大小能不能改變?
3.3 請簡述順序查找法的過程?
4.編程題
4.1 定義整形數(shù)組,存放5個學(xué)生的成績【成績值自己設(shè)定】,將成績從大到小排序,獲得成績之和,平均成績,最小成績,最大成績。
4.2定義長度為10的整形數(shù)組,并存儲10個數(shù)字。再通過鍵盤輸入一個數(shù)字,使用二分查找法在數(shù)組中查找輸入的數(shù)字是否存在。
Java 數(shù)據(jù)結(jié)構(gòu)
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。