Minghsin University of Science and Technology
Uedu Main Site
Explore Uedu
Student Console
Register as Member/Login
Research Informed Consent Center
Survey Center
Educational Big Data Challenge
Claim or create a course
Try a Scenario Task
Support & Messages
Uptime Data

UeduGPTs

--

Jupyters

1

Local AI

--

Uedu Code

--

明新科大 AQI 70 24°C PM2.5 19
AI Reply Desktop Notifications

Show a desktop notification when the AI TA finishes replying

Chat Message Notifications

Notify me when classmates post messages in the forum

Sound notification

Play an alert sound whenever there is a new notification

Unit 06

陣列

一維與二維陣列、Arrays 工具類別、陣列是參照
Interaction實作 25 minute(s)

Learning Objectives

  • 用 new 或初始值清單建立陣列,說明各種型別的預設值,用索引與 length 存取元素
  • 用迴圈走訪陣列,完成加總、平均、找最大值這類常見工作,並避開索引越界
  • 說明陣列變數存的是參照:指派只複製參照,傳給方法後方法可以改陣列的內容
  • 使用 Arrays.toString、sort、fill、copyOf、equals,分辨 == 與 Arrays.equals
  • 建立與走訪二維陣列(包含每列長度不同的不規則陣列)

為什麼需要陣列

一個班有 40 位學生,要記錄每個人的分數。宣告 score1、score2……score40 四十個變數,不但寫起來累,也沒辦法用迴圈一次處理。 陣列(array)把多個同型別的值放在一起,用一個名稱加上索引(index)存取:

int[] scores = new int[40];   // 一次準備 40 個 int 的位置
scores[0] = 85;               // 第 1 個元素的索引是 0
scores[39] = 72;              // 最後一個元素的索引是 39

和 Python 的串列(list)相比,Java 的陣列有兩個重要差別:

Java 陣列 Python 串列
元素型別 全部相同,宣告時決定 可以混合
長度 建立時決定,之後不能改變 可以 append、remove
負數索引 不行,會拋出例外 a[-1] 是最後一個

需要可以增減長度的容器時,Java 用 ArrayList(第 13 課)。陣列是最基本的容器,也是 ArrayList 內部實際存放資料的方式。

建立陣列:new 與初始值清單

建立陣列有兩種寫法:

int[] counts = new int[4];          // 指定長度,元素自動填入預設值
int[] primes = {2, 3, 5, 7, 11};    // 初始值清單:直接列出元素,長度由元素個數決定

int[] 讀作「int 陣列」,是變數的型別。(int counts[] 也能編譯,這是從 C 語言沿用的寫法,一般建議把 [] 放在型別旁邊。)

用 new 建立時,每個元素都會有預設值,和物件欄位的預設值相同:

元素型別 預設值
int、long、short、byte 0
double、float 0.0
boolean false
char '\u0000'(編號 0 的字元)
String 與其他參照型別 null
  • 陣列.length 是元素個數;注意它是欄位,後面沒有小括號(字串的 length() 才有)
  • 合法的索引是 0 到 length - 1,最後一個元素是 a[a.length - 1]
  • 超出範圍會拋出 ArrayIndexOutOfBoundsException,例如 Index 5 out of bounds for length 5
  • 初始值清單只能用在宣告的那一行;之後要重新指定,必須寫 primes = new int[]{1, 2, 3};

陣列的預設值與索引

每執行一個 new,右邊就多出一個陣列,格子下方的小數字是索引。注意四個陣列剛建立時的內容:int 是 0、double 是 0.0、boolean 是 false、String 是 null。左邊的變數只存一個指向陣列的箭頭(參照)。

走訪陣列

處理陣列幾乎都會配合迴圈。用索引走訪的標準寫法:

for (int i = 0; i < scores.length; i++) {   // 條件用 <,不是 <=
    sum += scores[i];
}

不需要索引、只要依序讀取時,可以用第 05 課的 for-each:for (int s : scores) { sum += s; }。 但 for-each 的 s 只是元素值的複本,在 for-each 裡寫 s = 0; 不會改到陣列;要修改元素,一定要用索引 scores[i] = …。

找最大值的常見做法:先假設第一個元素最大,再逐一比較,遇到更大的就換掉。 不要把 max 的初始值設成 0:陣列如果全是負數,0 會被誤當成最大值。

計算平均時要注意整數除法:sum / scores.length 兩邊都是 int,小數會被捨去;先把其中一邊轉成 double,寫成 (double) sum / scores.length。

總和、平均與最大值

看右邊陣列的哪一格正被 i 指到。max 一開始是第一個元素 68,遇到 92 時更新;第二個 92 不會更新,因為條件是 > 而不是 >=,所以 maxIndex 記的是第一個最大值的位置。最後一個迴圈用索引把每個元素加 5,看右邊格子的值一格一格改變。

想一想:預設值、長度與索引

1.

下面的程式會印出什麼?

int[] a = new int[3];
System.out.println(a[1] + a.length);
2.

下面的程式會怎樣?

String[] s = new String[2];
System.out.println(s[0].length());
3.

下面的程式會印出什麼?(注意 b 和 c 之間有兩個空格)

String[] parts = "a b  c".split(" ");
System.out.println(parts.length);
第 1 次作答

陣列是參照

陣列是物件,放在堆積上;陣列變數存的是指向陣列的參照(第 08 課會對一般物件說明同一件事)。這帶來幾個結果:

1. 指派只複製參照。 int[] b = a; 不會複製陣列,a 和 b 指向同一個陣列,透過 b 改元素,用 a 也看得到。 要得到一份獨立的副本,用 Arrays.copyOf(a, a.length) 或 a.clone()。

2. 傳給方法的是參照的複本。 方法拿到的參照指向同一個陣列,所以方法裡改元素,呼叫端看得到。 但如果方法讓參數改指向一個新陣列(data = new int[3];),只改了方法自己的那份參照,呼叫端的變數不受影響。 (方法的寫法在第 07 課詳細介紹,這裡先看一個 static 方法怎麼接收陣列。)

3. == 比較的是「是不是同一個陣列」。 兩個內容相同、分別建立的陣列,== 是 false。 比較內容要用 Arrays.equals(a, b)。陣列本身的 a.equals(b) 和 == 一樣只比參照,不要用。

另外,直接 System.out.println(a) 不會印出內容,而是印出像 [I@1b6d3586 這樣的文字([I 表示 int 陣列,@ 後面的數字每次執行可能不同)。要看內容請用 Arrays.toString(a)。

兩個變數、一個陣列

第 13 行之後,a 和 b 的箭頭指向同一個陣列。呼叫 doubleAll(a) 時,新框架的 data 也指向同一個陣列,方法裡改的就是 main 的那一個。第 20 行 Arrays.copyOf 才真的建立第二個陣列。

Arrays 工具類別

java.util.Arrays 提供許多處理陣列的 static 方法,使用前在檔案開頭寫 import java.util.Arrays;:

方法 作用
Arrays.toString(a) 把內容轉成 "[3, 7, 19, 42]" 這樣的字串
Arrays.sort(a) 由小到大排序,直接改變原陣列,沒有回傳值
Arrays.fill(a, v) 把每個元素都設成 v
Arrays.copyOf(a, n) 建立長度 n 的新陣列並複製內容;n 比較大時,多出來的格子是預設值
Arrays.copyOfRange(a, from, to) 複製索引 from 到 to - 1 的部分
Arrays.equals(a, b) 長度與每個元素都相等時回傳 true
Arrays.deepToString(m) 二維陣列用這個,toString 只會印出每列的參照

字串的方法也會產生陣列:

  • "apple,banana,cherry".split(",") 依分隔字串切開,得到 String[]
  • "java".toCharArray() 得到 char[],可以逐字修改後用 new String(letters) 組回字串(字串本身不能修改)

sort、copyOf、fill、split、toCharArray

第 9 行 Arrays.sort(nums) 執行後,右邊同一個陣列的內容直接變成排序後的順序,沒有建立新陣列。第 12 行 copyOf 建立長度 6 的新陣列,多出來的兩格是 0;接著 fill 只改 nums,bigger 不受影響。

二維陣列

二維陣列是「陣列的陣列」:外層陣列的每個元素,都是一個指向一維陣列的參照。

int[][] grid = new int[2][3];   // 2 列、每列 3 行,全部是 0
int[][] seats = {
    {1, 2, 3},
    {4, 5, 6}
};
seats[1][2]                     // 第 2 列、第 3 行,值是 6
  • grid.length 是列數,grid[r].length 是第 r 列的行數
  • 走訪時用巢狀迴圈,內層的條件寫 c < grid[r].length,每一列長度不同也不會出錯
  • 因為每一列是獨立的陣列,可以只指定列數:new int[3][],再逐列建立長度不同的陣列,稱為不規則陣列(jagged array),例如三角形的座位表
  • 印出內容用 Arrays.deepToString(grid)

二維陣列與不規則陣列

右邊的 grid 是一個長度 2 的陣列,裡面兩格各自用箭頭指向一列。後半段 new int[3][] 只建立外層,三格一開始都是 null;迴圈裡逐列 new,每列長度不同。

想一想:參照、比較與二維陣列

1.

下面的程式會印出什麼?

public class Main {
    static void reset(int[] a) { a = new int[]{0, 0}; }
    static void setFirst(int[] a) { a[0] = 0; }

    public static void main(String[] args) {
        int[] n = {5, 6};
        reset(n);
        System.out.print(n[0] + " ");
        setFirst(n);
        System.out.println(n[0]);
    }
}
2.

下面的程式會印出什麼?

int[] x = {1, 2};
int[] y = {1, 2};
System.out.println((x == y) + " " + Arrays.equals(x, y) + " " + x.equals(y));
3.

下面的程式會印出什麼?

int[] a = {1, 2, 3};
for (int v : a) {
    v = v * 10;
}
System.out.println(Arrays.toString(a));
4.

下面的程式會印出什麼?

int[][] m = {{1}, {2, 3}, {4, 5, 6}};
System.out.println(m[2].length + m[1][0]);
第 1 次作答

動手寫:一週氣溫

temps 是五天的最高氣溫。請找出最高與最低溫,並建立一個新陣列 reversed,把氣溫倒過來放(最後一天放在最前面),最後印出來。

你的輸出
預期輸出

重點回顧

  • 陣列存放多個同型別的值,長度建立後不能改變;new int[n] 的元素是預設值(0、0.0、false、null),初始值清單 {…} 直接給內容
  • 索引從 0 到 length - 1;length 是欄位,不加小括號;超出範圍拋出 ArrayIndexOutOfBoundsException
  • 用 for (int i = 0; i < a.length; i++) 走訪;for-each 只能讀,不能改元素
  • 陣列變數存的是參照:指派只複製參照,傳給方法後方法可以改內容;要獨立副本用 Arrays.copyOf
  • == 和陣列的 equals 只比較是不是同一個陣列,比內容用 Arrays.equals;印內容用 Arrays.toString
  • Arrays.sort 直接改變原陣列;split 產生 String[]、toCharArray 產生 char[]
  • 二維陣列是陣列的陣列,每列可以長度不同;走訪時內層條件用 grid[r].length,印出用 Arrays.deepToString
這一課完成了!
Next lesson:方法
你目前沒有登入,學習進度只存在這個瀏覽器。 Login 後,進度會跟著帳號,換電腦也看得到。