Java programing Java programing

Java 程式設計上課教材

Java 程式設計上課教材 > 課程內容 > 第 8 堂課 - 陣列初探

第 8 堂課 - 陣列初探

上次更新日期 2018/09/03

前面幾章我們談到資料時,都是一個變數一個值。那如果我們需要的資料量比較大,該如何處理?每個資料都給一個變數!那真的是崩潰了! 這個時候就得要使用陣列 (Array) 的方式來處理處理才行。陣列在未來你的資訊路上佔用的比重很大! 因為許多的資料都會用到陣列的概念!包括未來如果你要撰寫互動式網頁,在網頁讀寫資料庫時,主要就是透過陣列的方式來處理囉! 因此,我們在這裡先來理解一下簡單的陣列,尤其是二維陣列的部份。未來再來談其他的項目囉!

  • 8.0: 上週作業執行
  • 8.1: 在沒有陣列的情況下,數值資料處理的問題
  • 8.2: 什麼是陣列與陣列初探
  • 8.3: 用陣列進行排序:插入排序法
  • 8.4: 用 for-each 迴圈循序讀取陣列內容
  • 8.5: 實際練習題目
  • 8.6: 參考資料
  • 8.7: 課後練習

8.0: 上週作業執行

請從 class.vbird.tw 上面下載你上週的作業 java 文字檔,然後先行編譯成功後,等待老師過來查閱是否編譯成功與執行成功! 若執行成功,在你的作業成績會打 v !

8.1: 在沒有陣列的情況下,數值資料處理的問題

想要了解陣列的好處之前,先來了解一下,如果沒有陣列,會有什麼大問題!假設你有一個班級,裡面有 10 個學生的小班, 每個學生的成績你想要找出最佳、最差以及平均值,在沒有陣列的情況底下,通常你是如何處理的?大概是這樣:

  1. 分析問題:每個學生給予一個變數,例如 std01 ~ std10 這樣的資料,然後給予分數值。還得要透過 maxres 、 minres 與 avgres 三個變數, 分別來代表最佳、最差以及平均值。當然,計算的方式就得要持續處理!而且因為都是不同的變數,似乎無法使用單純的迴圈來處理。
  2. 解決步驟:
    1. 宣告類別名稱為 unit08_1_1 (就是檔名)
    2. 開啟方法為 main 的程式
    3. 宣告 10 個變數名稱,同時帶入變數整數的數值,代表學生的成績。
    4. 使用選擇的方式,進行 maxres, minres, avgres 的計算。
    5. 透過 System.out.println 將最佳、最差與平均值列出來。
    6. 關閉 main
    7. 關閉類別
  3. 程式設計:
    int std01 = 80;  // 先針對學生給予變數、型態與數值
    int std02 = 78; 
    int std03 = 65;
    int std04 = 82;
    int std05 = 77;
    int std06 = 91;
    int std07 = 55;
    int std08 = 65;
    int std09 = 93;
    int std10 = 85;
    int maxres = 0, minres = 100; // 假設最高分 0 分,最低分 100 分,然後依序處理
    double avgres = 0.0;          // 預先假設平均分數為 0 分
    maxres = ( maxres <= std01 ) ? std01 : maxres;  // 用來判斷最高分
    minres = ( minres >= std01 ) ? std01 : minres;  // 用來判斷最低分
    ...
    avgres = (std01+std02+std03+std04+std05+std06+std07+std08+std09+std10)/10.0; // 算平均
    System.out.println ("最佳成績" + maxres);
    System.out.println ("最差成績" + minres);
    System.out.println ("平均成績" + avgres);
    
  4. 編譯、執行與測試:開始測試執行流程。
    C:\Users\dic\java>java unit08_1_1
    最佳成績93
    最差成績55
    平均成績77.1
    

上面的案例應該不難理解,內容也很簡單,就是一堆算式而已。不過,真的很麻煩~同樣的算式需要一再的重複!明明就是學生的成績而已, 有必要用到這麼多的變數嘛?此外,如果再增加 15 個學生數呢?若以本班來說, 50 個同學耶!要有 50 個變數~還要外帶計算平均時,得要自己修改人數! 否則會出問題~這真是怪異!應該有更簡單的方式吧?

8.2: 什麼是陣列與陣列初探

大家都這個年紀了,應該有用過 Excel / libreoffice 之類的電子試算表軟體。在該種軟體中,我們可以發現到,每一個數值都可以放置在一個欄位內, 而每個欄位都有一個索引。例如做左上方的欄位名稱為 A1,橫列則為 B1, C1, D1..,直行則為 A2, A3, A4... 以此類推。以圖示來看,有點像底下這樣:

電子試算表示意圖

其實,這就是陣列的應用!想像一下,你用 Excel 第一直行來紀錄學生的成績,因此學生的成績就紀錄在 A1, A2, A3... 到 A10 這 10 個位置, 如果有需要增加其他的學生成績,就繼續往下累加即可。然後,需要計算的時候,將整個直行圈選起來,這樣所有的資料就能夠當作『一組變數的數據』來進行處理。 所以,你只要『一個變數』就能代表所有的數值!舉例來說,例如底下的圖示,有個變數名稱為 results,內容編號從 0 號開始, 每個欄位紀錄一個成績,最終你的 results 就代表所有的成績。當有更多的成績要加進來,就直接填到 6 號位置即可!就這麼簡單!這就是陣列。

陣列

陣列的好處是,他可以透過索引 (index) 的數值,去直接找出該欄位的資料來進行應用!舉例來說,上面的資料我如果需要找出第 4 個學生,就是第 3 號 (因為從 0 開始編號之故),那我只要呼叫 results[3] 就能夠直接找到該位置的數據了!跟電子試算表很像~就是找出 A4 的欄位資料的意思! 很容易理解~不是嘛!唯一的差別是,Java 陣列使用的是中括號加上數值的樣式而已。

軟體欄位名稱-1欄位名稱-2欄位名稱-3欄位名稱-4...
電子試算表A1A2A3A4...
Java 陣列變數res[0]res[1]res[2]res[3]...

這樣看起來就很容易了!接下來,只要將數據填上去,就能夠快速的應用了!

  • 陣列的宣告與填上數值:固定的陣列欄位數量

我們知道 Java 裡面要使用變數得要事先宣告,那麼陣列 (array) 要如何宣告呢?基本上的格式有兩種:

第一種:
int res[] = new int[50];

第二種:
int[] res = new int[50];

隨你的喜好來處理。鳥哥比較喜歡使用第一種方式,因為中括號直接連在變數後面,感覺比較熟悉。另外,那個 50 的意思,指的是要讓系統預先切出 50 個欄位! 因為欄位數量的使用與記憶體的分配有關,所以雖然可以預留多一點欄位數量,但是也不要超出太多,避免超量使用記憶體容量啊!

假設你要的陣列變數名稱為 score ,且內容為浮點數 (double),同時,需要的欄位數量共有 100 個,請問該如何在 Java 裡面進行宣告?

之後就是開始填上數值!填上數值的方式也很簡單:

res[0] = 80;
res[1] = 78;
res[2] = 65;
...

未來你只要掌握 res[這裡] 的數值之後,就可以透過迴圈來處理囉!現在,請將 unit08_1_1.java 的程式拿出來,使用上述的方式修改, 看看程式碼有沒有變短些!?

  1. 分析問題:以 res[] 作為陣列名稱,然後以迴圈的方式進行最佳、最差、平均成績的計算
  2. 解決步驟:
    1. 宣告類別名稱為 unit08_2_1 (就是檔名)
    2. 開啟方法為 main 的程式
    3. 宣告與建立及初始化 res[] 陣列成為整數型態,且給予 10 個欄位的陣列值
    4. 透過 for 迴圈的建立,來計算:
      • 最佳成績、最差成績以及平均成績。其中平均成績先以累加總和,最後再除以 10 來取得成績即可。
    5. 透過 System.out.println 開始輸出最終成績結果
    6. 關閉 main
    7. 關閉類別
  3. 程式設計:
    int res[] = new int[10];  // 宣告及初始化 res[] 陣列
    res[0] = 80;              // 給予陣列欄位的值
    res[1] = 78; 
    res[2] = 65;
    res[3] = 82;
    res[4] = 77;
    res[5] = 91;
    res[6] = 55;
    res[7] = 65;
    res[8] = 93;
    res[9] = 85;
    int maxres = 0, minres = 100;
    double avgres = 0.0;
    for ( int i=0; i <=9; i++ ){
    	maxres = ( maxres <= res[i] ) ? res[i] : maxres;  // 取得最大值
    	minres = ( minres >= res[i] ) ? res[i] : minres;  // 取得最小值
    	avgres = avgres + res[i];                         // 取得加總值
    }
    avgres = avgres / 10.0;
    
  4. 編譯、執行與測試:開始測試執行流程。
    C:\Users\dic\java>java unit08_2_1
    最佳成績 93
    最差成績 55
    平均成績 77.1
    

另外,需要特別留意,上面是 10 個學生,如果我填寫了『 int res[] = new int[20] 』呢?那沒用到的欄位內容是什麼?在初始值方面, 如果是數值的話,預設會是 0 喔!如果是字串的話,預設則是 \u0000,如果是布林值,則預設為 false 囉!所以,最好不要使用自動抓取陣列數量的方式來做迴圈, 否則會出問題喔!

  • 陣列的宣告與填上數值:利用陣列初始化器進行處理

剛剛前面的案例當中,基本上,還是一個一個填值,這樣的設定還是有點煩躁~畢竟後續就是一堆數值而已~那如果還得要有個 50 個學生,這樣一個一個輸入, 感覺上還是有點複雜。對於這種基本數值的設定方式,還有個簡單的陣列內容宣告方式,就是底下這樣的模樣:

int res[] = {80, 78, 65, 82, ...};
res.length  // 取得此陣列的欄位數量,注意, res 陣列名稱並沒有 [] 喔!

這是固定格式,必需要寫在同一行才行!所以,如果是這樣的設定方式,那麼將 unit08_2_1.java 另存成 unit08_2_2.java ,變更一下設定吧。 另外,這種方式的好處是,在陣列的欄位上面,可以透過 res.length 的方式來取得,而不用採固定的數值。因此程式碼的撰寫會變得更有彈性! 因為,如果有 50 個人的話,只要在上述程式碼的第一行,寫入大括號 {} 裡面,就好了!不用修改任何程式喔!

int res[] = {80, 78, 65, 82, 77, 91, 55, 65, 93, 85};
....
for ( int i=0; i <=res.length-1; i++ ){
....
avgres = avgres / res.length;
在上面的程式碼當中,為什麼 for 迴圈裡面,『 i <= res.length-1 』呢?不是總共有 10 個嘛?為什麼要 -1 呢?

8.3: 用陣列進行排序:插入排序法

通常使用陣列的情況下,我們可能會進行所謂的『排序』這個功能!簡單的數值排序主要有泡沫排序與插入排序兩種方法, 其中又以插入排序法速度較快,因此底下我們就來介紹一下插入排序法。

插入排序法一般是由小排到大,也就是說,如前面的案例 (十個成績) 所示,第二個成績與第一個比,若第二個比較小,則兩者的數值替換, 若第一個比較小,則不動作。之後則是第三個來跟前兩個比,之後則是第四個來跟前三個比,依此類推。比較的情況下,如果比前面的小,就更換, 如果比前面的大,就不動作。wiki 有個很棒的動畫圖示,大家可以前往查看一下:

  • wiki 的插入排序法 (當中的數字排序動畫一定要看)
  • 要注意的是,預設第一個不動,從第二個跟前面做比較而來。當然,你也可以從大排到小~只要修改一下 < 或 > 的情況即可。 在 wiki 內的演算法說明如下:

    1. 從第一個元素開始,該元素可以認為已經被排序 (假定為陣列 old )
    2. 取出下一個元素 (假定為 mynew 變數,且為第 i 號元素) ,在已經排序的元素序列中從後向前掃描,進入 j=i-j 號元素到 0 號 (old[j]) 之間的迴圈掃描
      1. 如果 old[j] 元素大於新元素 (mynew),將該 old[j] 移到下一位置,亦即是 old[j+1] = old[j]
      2. 重複步驟 A ,直到找到已排序的 old 元素小於或者等於新元素的位置 (假設為 j 號碼)
      3. 將新元素 mynew 插入到該位置,亦即是 old[j] = mynew
    3. 回到第 2 步驟,進行下一個元素的排序,直到元素排序完畢

    現在,請透過上述的說明,將 unit08_2_2.java 另存新檔為 unit08_3_1.java ,然後將數據進行排序,最終印出到螢幕上。基本的排序程式碼會有點像這樣:

    int res[] = {80, 78, 65, 82, 77, 91, 55, 65, 93, 85};
    int mynew, i, j;
    for ( i=1; i < res.length; i++ ){
    	mynew = res[i];                                // 步驟 2 的意思,取出新元素 (mynew)
    	for ( j=i-1; j>=0 && res[j] > mynew ; j-- ) {  // 進入舊元素判定的迴圈中
    		res[j+1] = res[j];                     // 就是步驟 A 的意思!
    	}
    	res[j+1] = mynew;                              // 步驟 B 與 C,為什麼要 j+1 呢?
    }
    

    注意看到主程式碼的部份:

    • 在內層的 for 迴圈當中,第二個結束條件有雙重條件 (j>=0 && res[j] > mynew) ,亦即是除了 j 要大於等於 0 之外,同時得再加上步驟 A 當中談到的必需要大於新元素才行。在此條件下, 步驟 A 就成立了!因此就進入 res[j+1] = res[j] 的設定!
    • 如果沒有在此條件下 (就是 j 等於 0 了,或者是 res[j] 小於 mynew 了),就會進入到步驟 B 與 C 的設定。 此條件有兩個可能:
      • 一個是找到 j 值了 (找到位置處)
      • 一個是即使是 j=0 也找不到!此時 j 會成為 -1 啦!
      只是為什麼是『 res[j+1] = mynew 』呢?這是因為內層 for 迴圈中,只要進行過,那個 j 就會減 1 (j--的緣故), 因此,當下找到的是 j,不過跳出迴圈後,該值會少 1 喔!所以當然要加回來的意思!

    最終執行程式碼,會輸出這樣:

    C:\Users\dic\java>java unit08_3_1
    55
    65
    65
    77
    78
    80
    82
    85
    91
    93
    
    透過這個排序法,我們很輕鬆的就可以進行小到大的排序!那如果要從大到小排序呢?請將 unit08_3_1.java 修改成為 unit08_3_2.java, 當執行『 java unit08_3_2 』時,會反向輸出,亦即是變成由大到小的排序方式喔!
    • 擴充排序功能

    如果你要的不只是成績,同時是該成績的同學學號,那該如何是好?舉例來說,假如還有個陣列為學生的學號,陣列名稱為 stdname , 且內容大致為這樣:

    String stdname[] = new String[10];
    stdname[0] = "4070C001";
    stdname[1] = "4070C002";
    stdname[2] = "4070C003";
    stdname[3] = "4070C004";
    stdname[4] = "4070C005";
    stdname[5] = "4070C006";
    stdname[6] = "4070C007";
    stdname[7] = "4070C008";
    stdname[8] = "4070C009";
    stdname[9] = "4070C010";
    

    請拿出 unit08_3_2.java 另存新檔為 unit08_3_3.java,之後加上這個學號的資料 (當然,一開始對應的學號與成績是成對的!亦即是 4070C001 對應了 80, 4070C002 對應了 78 分數,以此類推),然後最終以分數從高到低排序,同時列出學生的學號,當執行『 java unit08_3_3 』時,大致出現如下的畫面即可:

    C:\Users\dic\java>java unit08_3_3
    93, 4070C009
    91, 4070C006
    85, 4070C010
    82, 4070C004
    80, 4070C001
    78, 4070C002
    77, 4070C005
    65, 4070C003
    65, 4070C008
    55, 4070C007
    

    總之,陣列可以方便我們進行大量數據的處理,是相當有幫助的喔!

    8.4: 用 for-each 迴圈循序讀取陣列內容

    除了透過 res.length 或者是固定的陣列數量來進行 for 迴圈的輸出之外,Java 提供一個很簡單方便的『循序讀取陣列內容』的 for 迴圈! 我們知道迴圈裡面會有個變數,例如:

    for ( i=1; i< res.length; i++ ){
    	以 i 為索引值,進行 res[i] 的運算行為;
    }
    

    所以在迴圈內的變數為 i ,主要是索引值。現在我們這裡介紹一個循序讀取陣列內容的 for 迴圈,基本上,這個 for 迴圈的語法如下:

    for ( int u : res ) {
    	以 u 代替 res[0], res[1], res[2]... 依序處理
    }
    

    在上述的迴圈當中,產生一個新的變數,變數名稱為 u (當然,變數名稱是可以隨意設定的,至於變數型態,則需要與陣列的型態相同!), 然後只要將此變數印出來,就可以得到循序輸出的結果了。現在,請將 unit08_3_2.java 取出來, 並且另存新檔為 unit08_4_1.java,然後將輸出時候的迴圈修改成上述模樣,編譯後印出來!結果會與 unit08_3_2 相同!

    8.5: 實際練習題目

    陣列真的很重要!剛剛接觸大家應該還不熟悉,所以,實做練習幾次加強印象最重要

    • A. 根據使用者輸入的數值輸出月份資料

    假設我們提供一個輸入界面,讓使用者輸入 1~12 的數值,根據此數值我們提供對應的英文月份名稱 (January, February, March, April, May, June, July, August, September, Octoer, November, December),以及縮寫 (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec),亦即提供兩個名稱這樣。該如何處理?

    1. 分析問題:給兩個陣列名稱,例如 lname 及 sname (long 與 short 的縮寫),之後輸入上述的名字。再根據使用者輸入的數值作為索引 (index) 去呼叫月份。 唯一要注意的是,因為陣列排序是由 0 號開始編號,因此 index 數值必需要 -1 才對喔!
    2. 解決步驟:
      1. 匯入 java.util.Scanner 套件
      2. 宣告類別名稱為 unit08_5_1 (就是檔名)
      3. 開啟方法為 main 的程式
      4. 建立名為 lname 與 sname 的英文名稱陣列
      5. 建立名為 input 的 Scanner 物件
      6. 提示使用者輸入 1~12 這幾個數值
      7. 根據使用者輸入的數值去呼叫正確的月份名稱
      8. 關閉 main
      9. 關閉類別
    3. 程式設計:
      String lname[] = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "Octoer", "November", "December"};
      String sname[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
      
    4. 編譯、執行與測試:開始測試執行流程。
      C:\Users\dic\java>java unit08_5_1
      輸入 0~12 的數值,0 結束,1~12 會顯示對應的月份英文名: 1
      完整名稱:January
      縮寫名稱:Jan
      輸入 0~12 的數值,0 結束,1~12 會顯示對應的月份英文名: 3
      完整名稱:March
      縮寫名稱:Mar
      輸入 0~12 的數值,0 結束,1~12 會顯示對應的月份英文名: 12
      完整名稱:December
      縮寫名稱:Dec
      輸入 0~12 的數值,0 結束,1~12 會顯示對應的月份英文名: 15
      輸入 0~12 的數值,0 結束,1~12 會顯示對應的月份英文名: 0
      結束程式
      

    這個題目要注意的其實只有陣列的宣告以及透過迴圈反覆的執行月份的調查而已~還有得要注意預設的情況下,陣列的編號都是從 0 號開始, 所以使用者輸入的月份需要 -1 才會是索引 (index) 的數值!要注意!

    • B. 午餐的調查迴圈 (順便做排序)

    我們在第六章用了不少方式來討論午餐的選擇~當時的程式碼有點複雜~而且,如果有多出其他的餐館,或者是某些餐館已經不再營業時, 需要修改程式碼就很討厭~現在我們可以透過陣列的方式來修改~讓程式碼變得更簡單、好維護!

    1. 分析問題:透過陣列來進行午餐的店家選擇,同時,因為可能存在爭議,因此,每次都選擇出前三名店家。因為得要有三個不同的店家, 所以使用亂數制定索引時,就不能有重複的情況發生,因此還需要進行重複驗證的流程才行!
    2. 解決步驟:
      1. 因為要跟系統互動,我們這邊使用 JOptionPane 來進行結果的輸出,所以匯入 javax.swing.JOptionPane 套件
      2. 宣告類別名稱為 unit08_5_2 (就是檔名)
      3. 開啟方法為 main 的程式
      4. 設定名稱為 mystore 的陣列,且直接給予許多店家的名稱,這樣在設定上會比較方便!
      5. 指定名為 allstore 的字串變數,將所有店家以條列式的方式彙整為一個訊息 (用 for 迴圈累加 mystore)。
      6. 因為需要列出三個店家提供選擇,因此使用 myindex 陣列,且內容有 3 個欄位的宣告
      7. 進入 do {} while () 迴圈,讓用戶可以自由選擇是否需要重新篩選。
        1. 以 JOptionPane.showMessageDialog 顯示目前系統紀錄的所有店家資訊,讓使用者了解店家資訊
        2. 指定 myindex[0] 為一個索引數值的亂數內容 (myindex[0] = (int)(Math.random()*mystore.length);)
        3. 以 for 迴圈指定另外兩個 myindex 的數值,但須要判斷有沒有重複,因此:
          1. 取得新的陣列亂數值
          2. 以 while 迴圈,同時以類似 flag == 0 這樣的方式來處理有沒有重複
          3. 內層以 for 迴圈,檢查 ( j=i-1; j>=0; j-- ) 的 myindex[j] 項目喔!
          4. 若 flag 參數改變了,就跳出迴圈
      8. 以 okstore 作為三個店家的資訊彙整
      9. 顯示三個店家的資訊,然後讓用戶選擇是否繼續執行一次?
      10. 關閉 main
      11. 關閉類別
    3. 程式設計:
      // 店家資訊的示意:
      String mystore[] = {"壹街水餃", "賣噹噹", "鐵路便當", "大雅", "絕鼎炒飯", "正樽當歸鴨", "日之食堂", "魚小璐", "八方雲集", "唐家泡菜館", "新丼飯堂", "杜桑灶咖"};
      
      // 店家資訊彙整成為一個訊息的示意:
      String allstore = "";
      for ( int i=0; i<mystore.length; i++){
      	allstore = allstore + "- " + mystore[i] + "\n";
      }
      
      // 檢查以陣列確定亂數有沒有重複的示意:
      myindex[0] = (int)(Math.random()*mystore.length);   // 第一個亂數
      for ( i=1; i<=2; i++){
      	flag = 0;
      	while ( flag == 0 ) {
      		myindex[i] = (int)(Math.random()*mystore.length);  // 新的亂數
      		flag = 1;
      		for ( j=i-1; j>=0; j--){                           // 與舊的亂數比對
      			if ( myindex[i] == myindex[j] ) flag = 0;
      		}
      	}
      }
      
    4. 編譯、執行與測試:開始測試執行流程,執行『 java unit08_5_2 』會出現類似如下的樣式: 以陣列處理午餐調查 以陣列處理午餐調查

    這個範例在『確認亂數有沒有重複』那個地方也是個關鍵,雖然只有三個數值,你可以簡單的透過 if 去處理,不過,如果未來還有很多的數值, 最好還是先學會使用雙層迴圈的方式來調查會比較妥當!

    • C. 處理指定陣列運作的程序

    某些時刻你必須依據業主的要求來完成各項計算的工作。舉例來說,請完成底下的陣列運算並輸出結果。

    • 計算 sum = a[0]*b[9]+a[1]*b[8]+....+a[9]*b[0]
    • a[]={4,-5,3,5,9,-3,2,8,7,-4}
    • b[]={8,4,3,-3,0,9,1,3,2,9}

    事實上,這個題目的解決方法並不困難,因為兩者的索引個數都是 10 個,而且要互相乘上的元素索引相加為 9,所以處理起來並不困難, 主要的重點程式碼大概是這裡:

    for ( int i=0; i<=9; i++ ) {
    	sum = sum + a[i] * b[9-i];
    }
    

    最後執行的結果應該會出現類似如下的數值:

    C:\Users\dic\java>java unit08_5_3
    總和結果為: 135
    
    • D. 讓使用者輸入預計取得的亂數個數,然後分析這些數值的相關資料

    像老師需要統計成績,然後直接輸入成績在程式當中,由程式來進行計算等等的工作,找出最佳成績、最差成績、以及最佳與最差成績的座號 (就是第幾個輸入的號碼,我們可以從索引值來決定。只是我們當然都知道,索引值是從 0 號開始的!)。另外,我們也需要找出平均值。 最後,計算最佳與最差成績的差值。

    1. 分析問題:因為成績不是寫入程式碼當中,而是由操作者輸入。因此,在設計陣列的欄位就要特別注意! 通常的作法就是讓使用者自行輸入數量 (例如輸入一個名為 nu 的整數變數),然後再處理陣列的宣告即可。
    2. 解決步驟:
      1. 因為需要使用者輸入成績,因此需要匯入 java.util.Scanner 物件才行。
      2. 宣告類別名稱為 unit08_5_4 (就是檔名)
      3. 開啟方法為 main 的程式
      4. 建立名為 input 的 Scanner 物件
      5. 提供使用者輸入『學生數』的整數變數,名稱設為 nu
      6. 宣告名為 score[] 的陣列,陣列數量就是前一個程式碼,使用者輸入的 nu 變數
      7. 進入 for 迴圈,開始輸入成績
        1. 輸入成績,並且指定為 score[i] 這樣的格式
        2. 分析是否為最大值 (max),若為最大,則指定 max=score[i] 以及 max_index=i 這樣。
        3. 同理,分析是否為最小,並分別指定 min 與 min_index 的數值
        4. 進行加總,例如 sum = sum + score[i]
      8. 建立名為 avg 的 double 型態變數,內容則是 sum / nu ,計算出平均值。
      9. 透過 System.out.println 開始輸出結果
      10. 關閉 main
      11. 關閉類別
    3. 程式設計:其實都很簡單,所以這裡就不解釋
    4. 編譯、執行與測試:開始測試執行流程。
      C:\Users\dic\java>java unit08_5_4
      你共有幾個學生: 10
      輸入第 1/10 個學生成績: 70
      輸入第 2/10 個學生成績: 80
      輸入第 3/10 個學生成績: 95
      輸入第 4/10 個學生成績: 70
      輸入第 5/10 個學生成績: 65
      輸入第 6/10 個學生成績: 85
      輸入第 7/10 個學生成績: 45
      輸入第 8/10 個學生成績: 98
      輸入第 9/10 個學生成績: 100
      輸入第 10/10 個學生成績: 64
      最佳成績: 100
      最佳成績座號: 9
      最差成績: 45
      最差成績座號: 7
      全班平均: 77.0
      最高最低落差: 55
      

    這個題型主要是提供了使用者可以輸入資料成為陣列的方法!那麼成為陣列之後,就可以進行很多很多任務了!可以參考課後作業來進行額外陣列的排序等任務。

    • E. 撲克牌洗牌程式的處理

    在網路上我們經常看到紙牌程式,而我們也知道撲克牌共有 52 張分為 4 種花色,每種花色有 1 ~ 13 (A, 2, 3... 10, J, Q, K)。 而 4 種花色分別是黑桃 (♠)、紅心 ()、方塊 ()與梅花 (♣)。 那麼如何進行洗牌的行為呢?有個簡單的概念如下:

    • 假設每張排都有一個號碼,假設如下的排序:
      • 0 號為黑桃 A, 1 號為黑桃 2,12 號為黑桃 K
      • 13 號為紅心 A,25 號為紅心 K
      • 26 號為方塊 A,38 號為方塊 K
      • 39 號為梅花 A,51 號為梅花 K
    • 開始透過亂數『 Math.random()*52 + 1 』取得 0~51 之間的號碼 (共 52 個),當取得該號碼之後,該號碼就會被佔用,此時該號碼不能重複! 因此,透過另一個陣列來紀錄排面的號碼。而此陣列則為洗牌後的順序。

    現在,請依據上面的想法,設計出一個程式碼,該程式碼會自動幫你洗牌~洗牌完成之後,再將所有的排面由第一張開始輸出。 另外,各種花色的英文定義為:

    • 黑桃 spades
    • 紅心 hearts
    • 方塊 diamonds
    • 梅花 clubs

    那就開始來洗牌吧!

    1. 分析問題:假設每張牌都有個號碼,該號碼有沒有被用到就可以視為該牌面是否已經在位置上了。因此,可以先處理一個名為 num[] 的陣列。 因此,需要宣告為 num[52] !再指定一個名為 card[] 的陣列,宣告為 card[52],這個 card 就是洗牌之後每個位置的牌號。
    2. 解決步驟:
      1. 宣告類別名稱為 unit08_5_5 (就是檔名)
      2. 開啟方法為 main 的程式
      3. 宣告 num 陣列為 52 個欄位的整數
      4. 宣告 card 陣列為 52 個欄位的整數
      5. 宣告 cardname 陣列為 {"黑桃", "紅心", "方塊", "梅花" },就是花色囉!
      6. 初始化 num 與 card,請均設定預設值為 0 (再次規範初始值)
      7. 開始進入牌面的處理,這需要使用 for 迴圈來處理 52 次:
        1. 先定義一個名為 flag 的旗標變數,假定為 0 ,亦即該牌面還需要重新處理
        2. 透過 x = (int)(Math.random()*52); 取得一個 0~51 之間的數值喔!
        3. 檢查 num[x] 是否為 0,若為 0 則代表該牌面沒有被使用,此時:
          • 宣告 num[i] = 1 ,亦即被使用了
          • 宣告 card[i] 為 x
          • 宣告 flag = 1 ,代表該牌面處理完畢
      8. 進入 for 迴圈,開始進行牌面的輸出
      9. 關閉 main
      10. 關閉類別
    3. 程式設計:
      // 檢查牌面的重點:
      flag = 0;
      do {
      	x = (int)(Math.random()*52);	// 亂數取得一個牌面
      	if ( num[x] == 0 ) {		// 該牌面沒有被使用,那才可以指定!
      		card[i] = x;
      		num[x] = 1;
      		flag = 1;
      	}
      } while ( flag == 0 ) ;
      
      // 輸出的重點:
      for ( i=1; i<52; i++ ) {
      	System.out.println ("第 " + i + " 牌的花色是: " + cardname[card[i]/13] + (card[i]%13+1));
      }
      
    4. 編譯、執行與測試:開始測試執行流程(每次輸出都會不一樣喔!)
      C:\Users\dic\java>java unit08_5_5
      第 1 牌的花色是: 方塊10
      第 2 牌的花色是: 黑桃13
      第 3 牌的花色是: 方塊12
      第 4 牌的花色是: 黑桃2
      第 5 牌的花色是: 梅花12
      第 6 牌的花色是: 梅花2
      第 7 牌的花色是: 方塊9
      第 8 牌的花色是: 方塊2
      第 9 牌的花色是: 方塊13
      第 10 牌的花色是: 黑桃6
      ...
      第 46 牌的花色是: 紅心10
      第 47 牌的花色是: 黑桃1
      第 48 牌的花色是: 方塊5
      第 49 牌的花色是: 紅心8
      第 50 牌的花色是: 方塊4
      第 51 牌的花色是: 黑桃9
      第 52 牌的花色是: 紅心9
      

    這只是洗牌而已,那如果洗牌完畢之後,還想要處理分牌給四個使用者呢!?這可能就得要透過 4 個一維陣列來搞定! 後面的課後習題可以嘗試著玩玩看。

    8.6: 參考資料

    8.7: 課後練習

    先查閱 1.3 小節的介紹,了解雲端系統的登入、繳交資料檔名等等的設計,然後再繼續底下的習題。 最終要上傳的檔案有:

    • unit08-4070CXXX-你的名字.docx
    • unit08_score.java (純文字檔)
    • unit08_card.java (純文字檔)
    1. 完成上課的習題內容回答:
      1. 假設有個陣列名稱為 mycard ,這個陣列共需要 13 個整數,那麼該如何宣告這個陣列?
      2. 假設你不知道 mycard[] 的個數,請寫一個迴圈,將所有的 mycard 內容一行一個的顯示出來。
      3. 假設上述的 mycard[] 陣列需要進行從大到小的排序,請寫下插入排序法的語法
      4. 陣列是否有初始值?請說明整數、浮點數與字串類型的陣列之預設值為何?
    2. 取出 unit08_5_4.java 的程式碼,當初輸入成績並列出最佳、最差成績而已。在輸出這些重要資訊之後,請再透過從大到小的排序 (請參考插入排序法), 然後將所有同學的成績排序並列印出來,同時輸出名次與成績。
      C:\Users\dic\java>java unit08_score
      你共有幾個學生: 10
      輸入第 1/10 個學生成績: 70
      輸入第 2/10 個學生成績: 80
      輸入第 3/10 個學生成績: 95
      輸入第 4/10 個學生成績: 70
      輸入第 5/10 個學生成績: 65
      輸入第 6/10 個學生成績: 85
      輸入第 7/10 個學生成績: 45
      輸入第 8/10 個學生成績: 98
      輸入第 9/10 個學生成績: 100
      輸入第 10/10 個學生成績: 64
      最佳成績: 100
      最佳成績座號: 9
      最差成績: 45
      最差成績座號: 7
      全班平均: 77.0
      最高最低落差: 55
      第 1名的成績: 100
      第 2名的成績: 98
      第 3名的成績: 95
      第 4名的成績: 85
      第 5名的成績: 80
      第 6名的成績: 70
      第 7名的成績: 70
      第 8名的成績: 65
      第 9名的成績: 64
      第 10名的成績: 45
      
    3. 取出 unit08_5_6.java 的洗牌程式碼,在洗牌完成之後,依序分給四個玩家,然後將四個玩家的牌面依序列出來。假設我們還沒有學過二維陣列, 因此,四個玩家可分別使用 a0[13], a1[13].. 來設計為 4 玩家。此外,玩家的牌面請務必進行排序,方便查牌之用。
      C:\Users\dic\java>java unit08_card
      玩家1            玩家2            玩家3            玩家4
      黑桃5            黑桃2            黑桃1            黑桃4
      紅心1            黑桃3            黑桃10           黑桃7
      紅心3            黑桃6            黑桃11           黑桃8
      紅心5            黑桃9            黑桃13           黑桃12
      紅心10           紅心2            紅心4            紅心6
      方塊3            紅心9            紅心7            紅心11
      方塊4            紅心12           紅心8            方塊2
      方塊8            紅心13           方塊5            方塊6
      梅花1            方塊1            方塊10           方塊7
      梅花6            方塊11           梅花2            方塊9
      梅花10           方塊13           梅花4            方塊12
      梅花11           梅花3            梅花8            梅花7
      梅花13           梅花5            梅花9            梅花12
      
    4. 進階題目:你可能玩過彈珠台,彈珠台大概就是會有如下的模樣的設計:
              .
             . .
            . . .
           . . . .
          . . . . .
         . . . . . .
        . . . . . . .
      
      | | | | | | | | |
      | | | | | | | | |
      | | | | | | | | |
      
      彈珠從上方放下來,途中會經過很多的釘子,每個釘子會有決定左、右的方向可能。以上面的圖示來看,這個球總共需要進行 7 次的判斷。 如果出現 7 次左,那麼當然就會降落到最左邊的位置上了。而且最多會有 8 個溝槽可以放置彈珠~ (就是釘子的排數 + 1 的意思)。 現在,請以這個樣式來處理,最多處理 10 顆彈珠,看看最終彈珠會怎樣的累積。執行結果會有點像這樣:
      C:\Users\dic\java>java unit08_balls
              O
      
              .
             . .
            . . .
           . . . .
          . . . . .
         . . . . . .
        . . . . . . .
      
      | | | | | | | | |
      | | | | | | | | |
      | | | | | | | | |
      | | | | | | | | |
      | | | | | | | | |
      | | | | | | | | |
      | | | |O|O| | | |
      | | | |O|O| | | |
      | | |O|O|O| | | |
      | | |O|O|O| | | |