數組

在日常生活中,可樂有罐裝的,有瓶裝的。這裏的“罐”和“瓶”就是可樂的容器。

Java當中也一樣,當同一類型的數據數量較多時,我們也可以通過容器將其裝在一起,更加方便使用。


數組是Java中的對象,用以存儲多個相同數據類型的變量。

數組能夠保存基本數據類型也能保存對象引用,但數組自身總是堆中的對象。


一、數組的創建

1.1、聲明數組:

通過說明數組要保存的元素類型來聲明數組,元素類型可以是基本數據類型或對象,後跟的方括號可以位於標示符的左邊或右邊。

也就是說,數組的聲明方式可以分爲以下兩種:

  • ArrayType ArrayName [];
  • ArrayType [] ArrayName;

符號“[]”代表聲明的是一個數組,這兩種聲明方式就達到的效果而言,沒有任何區別。

但第二種方式可以一次性聲明多個數組,如:int [] intArr1,intArr2;而且第二種方式的閱讀性更強。

所以通常來說,都選擇使用第二種方式來聲明一個數組對象。


1.2、構造數組:

當我們使用上面說到的方式聲明瞭一個數組後,實際上只是聲明瞭一個對應數組類型的對象引用,而內存中還沒有真正的數組對象存在。

這時候,就需要完成數組對象的構造工作。所以說,我們所謂的構造數組,實際上也就是是指在堆內存中真正的創建出數組對象。

而換句話說,所謂的構造數組的工作。就是指,在數組類型上執行一次new,從而完成數組的對象實例化工作。

爲了創建數組對象,JVM需要了解在堆內存上需要分配多少空間,因此必須在構造時指定數組長度。數組長度是該數組將要保存的元素的數量。


構造數組最直觀的方法是使用關鍵字new,後跟數據類型,並帶方括號“[]”指出該數組要保存的元素的數量,也就是數組長度。

例如:int [] intArray = new int[10];。該條語句代表聲明並構造了一個長度爲10的保存int類型數據的數組。

再次提醒,構造數組時必須要求聲明數組的長度,因爲我們已經說過了JVM需要得到這個長度,才能爲該數組對象在堆中分配合適的內存空間。

所以,不要使用int [] intArray = new int [];這樣的語句。這將會引起編譯錯誤。


現在我們通過一段代碼來看一看,數組在內存中的構造初始化特點:

[java] view plain copy
  1. private static void demo_1() {  
  2.     byte[] byteArr = new byte[5];  
  3.     short[] shortArr = new short[5];  
  4.     int[] intArr = new int[5];  
  5.     long[] longArr = new long[5];  
  6.     char[] charArr = new char[5];  
  7.     float[] floatArr = new float[5];  
  8.     double[] doubleArr = new double[5];  
  9.     String[] strArr = new String[5];  
  10.   
  11.     System.out.println("byteArr:" + byteArr[0]);  
  12.     System.out.println("shortArr:" + shortArr[0]);  
  13.     System.out.println("intArr:" + intArr[0]);  
  14.     System.out.println("longArr:" + longArr[0]);  
  15.     System.out.println("charArr:" + charArr[0]);  
  16.     System.out.println("floatArr:" + floatArr[0]);  
  17.     System.out.println("doubleArr:" + doubleArr[0]);  
  18.     System.out.println("strArr:" + strArr[0]);  
  19. }  
這段程序運行的輸出結果爲:

byteArr:0

shortArr:0

intArr:0

longArr:0

charArr:

floatArr:0.0

doubleArr:0.0

strArr:null

觀察輸出結果我們發現,對於數組構造,JVM在內存中還會根據該數組的數據類型對其中的元素進行一次默認初始化的賦值。其實這也正是源自於堆內存自身的特點。


對於在堆內存中存儲的變量,如果我們沒有在聲明變量時對其進行賦值工作。那麼堆也會對這些變量根據其自身數據類型進行一次默認初始化的賦值工作。

這也正是我初學Java時,一直不明白爲什麼一個類的成員變量可以不做手動的初始化賦值工作,仍然能夠在以後的程序中正常調用;

而如果一個局部變量如果不進行手動的初始化賦值,如果在之後的代碼對其發生調用,就會編譯出錯的原因所在。

因爲成員變量存儲在堆當中,即使我們沒有對其做手動的初始化賦值工作,其也會有一個默認的初始化值。而存儲在棧內存當中的成員變量則不會被進行默認初始化賦值工作,所以如果說我們沒有人爲的爲其指定一個初始化值的話,在之後對其調用時,該變量自身是沒有值的,自然無法調用。所以也就不難理解爲什麼會編譯出錯了。


多維數組

像我們前面說到的格式爲:int [] intArray = new int [5];這樣聲明的數組,被稱爲一維數組。那麼對應的,自然也就存在多維數組。

要明白的是:多維數組其實也是數組,只不過一維數組用於保存基本數據類型或對象引用,而多維數組用於保存數組(其實也是保存對象引用,數組的對象引用)。


所以,假設我們聲明一個二維數組:

int [ ] [ ] twoD = new int [5] [5];

對於這樣的多維數組,我們應該這樣理解:

一個int型的二維數組,實際上就是一個int型的一維數組(int [])的對象,而它保存的是一維數組的數組對象引用。

也就是說我們使用一個int型的二維數組,實際上存儲的元素就是:int [] intArray = new int [5];這樣的數組對象的引用。


所以就int [ ] [ ] twoD = new int [5] [5];而言,

實際上就是說,構造了一個int型的二維數組,該二維數組存儲5個“int [] intArray = new int [5]”這樣的一維數組。


如果覺得這樣的說法還是過於抽象,不易理解的話。我們不妨結合一些現實生活中的事物來看待:

舉個例子,我們在感冒或者中暑之類的時候,可能都喝過一樣東西叫:藿香正氣液。

就以藿香正氣液來說,我們知道它的最小包裝單位是“一小瓶”。我們可以將“一小瓶藿香正氣液”,視爲我們要存儲的數據元素。那麼:

假設一盒藿香正氣液裏面包裝有10小瓶,就正是所謂的一維數組:

藿香正氣液 [ ] 一盒 = new 藿香正氣液 [10];

假設一箱藿香正氣液裏面包裝有50盒,這種情況就是所謂的二維數組:

藿香正氣液 [ ] 一箱 = new 藿香正氣液 [50] [10];

同樣的原理,更多維的數組也可以以此類推。所以需要明白的就是:所謂的多維數組,根本來講還是數組。


1.3、數組元素的賦值與取值

既然知道了數組是作爲存放統一數據類型的容器存在的,那麼所謂容器,自然就涉及到向容器中存放元素或從中取出元素的操作的。

Java中,對於數組元素的訪問方式很簡單,數組中的各個元素都能通過索引(也就是數組下標)進行訪問,格式爲:arrayName[下標]。

而需要注意的就是,Java中數組元素的索引是從0而不是從1開始的,也就是說第一個被存儲進行數組的元素的索引是0而不是1;

相對的,數組中最後一個元素的索引就是聲明的數組的長度減去1,而不是聲明的數組長度。如果通過無效的索引訪問數組,則會觸發數組越界異常。

具體的使用,通過一段簡單的代碼進行簡單的演示

[java] view plain copy
  1. private static void demo_2() {  
  2.     int[] array = new int[5];  
  3.     // 可以通過length獲取數組的長度  
  4.     System.out.println("數組的長度爲:" + array.length);  
  5.   
  6.     // 遍歷數組中的元素,併爲其賦值  
  7.     for (int i = 0; i < array.length; i++) {  
  8.         array[i] = i + 1;  
  9.     }  
  10.     // 遍歷獲取數組中的元素的值  
  11.     for (int i = 0; i < array.length; i++) {  
  12.         System.out.println("array[" + i + "] = " + array[i]);  
  13.     }  
  14. }  


話到這裏,我們已經知道了在Java當中:

1、通過arrayType [] arrayName;可以聲明一個數組。

2、通過arraryName = new arrayType[length];可以對聲明的數組在內存中進行構造工作,並完成元素的一次默認初始化。

3、通過arrayName[index]對數組中存放的元素進行賦值或訪問。


那麼,順帶一提的就是,Java種還提供另外一種聲明方式。這種聲明方式將數組的聲明、構造以及賦值(顯式初始化)工作都集合到一條語句當中。

這種聲明方式就是:arrayType [ ] arrayName = {value1,value2,value3};。

舉例來說,如果我們想聲明一個int型的數組對象,其數組長度爲4,我們想要存放的4個值分別爲1,3,6,9。那麼就可以定義爲:nt [] intArray = {1,3,6,9};

使用這種方式最大的特點就在於:可以在聲明數組的同時,就完成數組中的元素的賦值工作。


除此之外,還有另外一種數組的聲明方式,被稱爲:匿名數組創建。

舉例來說,我們這樣定義:int [ ] intArray = new int [ ]{1,2,3}; 。這裏的"new int [ ]{1,2,3}"就被稱爲匿名數組的創建。

你可能在想,使用這樣的創建方式相對於其它方式而言,好處是什麼?

好處就是:可以實時的創建出一個新的數組,而不需要將其賦給任何變量,就直接作爲參數傳遞給接受對應數組類型的方法。

我們通過一段代碼更形象的來理解關於“匿名數組”的這一特點:

[java] view plain copy
  1. //分別使用三種創建方式完成相同效果的數組創建工作  
  2. private void array_demo(){  
  3.     //第一種方式  
  4.     int [] array_1 = new int[3];  
  5.     array_1[0] = 1;  
  6.     array_1[1] = 2;  
  7.     array_1[2] = 3;  
  8.     //第二種方式  
  9.     int [] array_2 = {1,2,3};  
  10.     //第三種方式  
  11.     int [] array_3 = new int[]{1,2,3};  
  12.       
  13.     /* 
  14.      * 第三種方式最大的好處就在於: 
  15.      * 直接實時創建匿名數組對象作爲方法參數進行傳遞 
  16.      * 而不需要像第一和第二種方式必須通過變量(對象引用)進行傳遞。 
  17.      */  
  18.     this.acceptUnnamedArray(new int[]{1,2,3});  
  19. }  
  20.   
  21. private void acceptUnnamedArray(int [] arr){  
  22.     //...  
  23. }  
我想順帶說明一下的就是,之所以被稱爲“匿名”數組,就是因爲“new int [ ]{1,2,3}”直接作爲方參數進行傳遞,

而沒有將自身賦給任何對象引用,也就是說該數組對象自身是沒有標示符的,我們知道在Java中,標示符就是一個變量的名字。

既然它沒有對應的“名字”,自然就被稱爲“匿名”了。而同理的,Java中的匿名對象也是如此。

與此同時數組本身也屬於對象,所以匿名數組其實也可以被視作是匿名對象的一種。



數組排序

很多時候,我們會需要對數組中的元素按照一定的順序(如按從小到大順序等等)進行重新排列。

所謂的排序,也就是指將元素按照指定順序進行位置的置換。所以,在正式的排序工作之前,我們應該首先了解數組中元素的置換工作怎樣完成。

數組中元素的置換過程與我們傳統的思想可能有些不同:

以學生相互換座位之間的問題爲例,座位號爲10的a學生與座位號爲25的b學生進行座位的互換,其過程被分解一下,其實就是:

首先,讓兩位學生都起身離開座位。然後讓a學生先坐到25號座位,b學生坐到10號座位,就完成了座位的置換。

而如果我們以相同的思想,對數組中的元素進行置換工作的話,對應代碼的體現形式就是:

[java] view plain copy
  1. package com.tsr.j2seoverstudy.base;  
  2.   
  3. public class ArrayDemo {  
  4.     public static void main(String[] args) {  
  5.         int [] arr = {1,2};  
  6.         System.out.println("置換之前:");  
  7.         printArr(arr);  
  8.         //進行元素置換  
  9.         swap(arr, 01);  
  10.         System.out.println("置換之後:");  
  11.         printArr(arr);  
  12.     }  
  13.   
  14.     /** 
  15.      * 數組元素置換 
  16.      * @param arr 數組 
  17.      * @param a 要置換的第一個元素的索引(下標) 
  18.      * @param b 要置換的第二個元素的索引 
  19.      */  
  20.     private static void swap(int [] arr,int a,int b){  
  21.         arr[a] = arr[b];  
  22.         arr[b] = arr[a];  
  23.     }  
  24.       
  25.     private static void printArr(int [] arr){  
  26.         for (int i = 0; i < arr.length; i++) {  
  27.             System.out.println("arr["+i+"] = " +arr[i]);  
  28.         }  
  29.     }     
  30. }  

但是我們運行該程序,發現其輸出結果爲:


也就是說,置換後兩個元素的值都變爲了2。出現這樣的錯誤,原因並不難理解:

首先arr[a] = arr[b],實際上執行的就是arr[0] = 2;於是這個時候內存中該數組內索引爲0和爲1的兩個元素的值實際上都變爲了2.

所以當再執行arr[b] = arr [a]的時候實際上就對應於arr[1] = 2;然後通過分析,我們發現:

之所以出現這樣的輸出結果,是因爲原本的索引0的元素的值"1"在置換過程中丟失了。

那麼我們應該做的措施就是讓記錄下這個值,不讓其丟失。所以,就可以通過新建一個臨時變量專門記錄該值的方式,避免丟失的發生。

於是上面的swap方法,應該修改爲:

[java] view plain copy
  1. private static void swap(int [] arr,int a,int b){  
  2.     //定義一個臨時變量記錄索引a的元素的值  
  3.     int temp = arr [a];  
  4.     arr[a] = arr[b];  
  5.     arr[b] = temp;  
  6. }  

再次運行程序,查看其輸出結果:


瞭解了數組元素的置換工作,就可以試着完成對數組的排序了。這裏我們對兩種原理較簡單但又很常用的排序方式進行了解。

第一種:選擇排序

原理:每一趟從待排序的數組中選出最小(或最大)的一個元素,順序放在已排好序的數列之後的一個位置,直到全部待排序的數據元素排完。


以我們日常生活中,按從矮到高的順序站隊列來說,假設現在有十個高矮不一的人需要排列。選擇排序的方式就是:

首先我們假定現在站在最左邊的第一個人就是最矮的,然後讓他分別與後面的九個人依次進行比較。

在一次比較過程完成後,記錄下這次比較中最矮的人和他站的位置。然後讓這個人改變位置站到最左邊去,而最初站在最左邊的人則站到這個人的位置上去。

然後因爲已經選出了最矮的人站在了最左邊了,這次就直接將最左邊第二個人選出,分別與剩下的8個人進行比較,然後以此類推。體現代碼中的表現形式就是:

[java] view plain copy
  1. public static void main(String[] args) {  
  2.     int[] arr = { 7681192435 };  
  3.     selectSort(arr);  
  4.     for (int i = 0; i < arr.length; i++) {  
  5.         System.out.print(arr[i]);  
  6.         if (i < arr.length - 1)  
  7.             System.out.print(",");  
  8.     }  
  9. }  
  10.   
  11. private static void selectSort(int[] arr) {  
  12.     int temp; //緩存  
  13.     int min;  //最小值  
  14.     int index;//最小值的索引  
  15.   
  16.     for (int i = 0; i < arr.length; i++) {  
  17.         min = arr[i];  
  18.         index = i;  
  19.         //依次比較  
  20.         for (int j = i + 1; j < arr.length; j++) {  
  21.             if (arr[j] < min) {  
  22.                 min = arr[j];  
  23.                 index = j;  
  24.             }  
  25.         }  
  26.         //位置置換  
  27.         temp = arr[i];  
  28.         arr[i] = min;  
  29.         arr[index] = temp;  
  30.     }  
  31. }  
查看其輸出結果爲:19,24,35,76,81

第二種:冒泡排序

原理:冒泡排序也是一種交換排序算法。其過程是數組中較小(或較大)的元素看做是“較輕的氣泡”,對其進行上浮操作。從底部開始,反覆的對其進行上浮操作

而對應於我們剛纔談到的隊列問題,冒泡排序的方式就是:依次讓相鄰的兩個人之間進行比較,如果左邊的人高於右邊的人,則讓他們交換位置。對應的代碼體現則是:

[java] view plain copy
  1. public static void main(String[] args) {  
  2.     int[] arr = { 7681192435 };  
  3.     bubbleSort(arr);  
  4.     for (int i = 0; i < arr.length; i++) {  
  5.         System.out.print(arr[i]);  
  6.         if (i < arr.length - 1)  
  7.             System.out.print(",");  
  8.     }  
  9. }  
  10.   
  11. private static void bubbleSort(int [] arr){  
  12.     int temp;  
  13.     for (int i = 0; i < arr.length - 1; i++) {  
  14.         /* 
  15.          * 數組長度-1是爲了保證不發生數組越界 
  16.          * 而減-i則是因爲每完成一次排序過程,該次比較中最大的數就會下沉到相對最後的位置, 
  17.          * 那麼就不需要進行重複而多餘的比較工作了,-i就是爲了避免這些多餘的工作。 
  18.          */  
  19.         for (int j = 0; j < arr.length - 1 - i; j++) {  
  20.             if(arr[j]>arr[j+1]){  
  21.                 temp = arr[j];  
  22.                 arr[j] = arr[j+1];  
  23.                 arr[j+1] = temp;  
  24.             }  
  25.         }  
  26.     }  
  27. }  

對於數組的理解和應用,到此就基本結束了。更多的使用方式還是應該根據實際的需求,自己加以利用。
發佈了33 篇原創文章 · 獲贊 13 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章