作為 Codegym 大學課程一部分的導師授課片段。報名參加完整課程。


“你好,阿米戈!”

“你好,瑞希!”

“你已經對數組了解一兩件事,我希望你甚至設法解決了一些任務。但你並不是什麼都知道。例如,這是關於數組的另一個有趣的事實。數組不僅是一維的(線性的) ). 它們也可以是二維的。

“嗯……什麼意思?”

“這意味著數組的單元格不僅可以代表一列(或行),還可以代表一個矩形表格。

int[][]name = new int[width][height];

"其中name是數組變量的名稱,width是表格寬度(以單元格為單位),height是表格高度。看一個例子:

int[][] data = new int[2][5];
data[1][1] = 5;
我們創建一個二維數組:2 列和 5 行。
我們在單元格 (1,1) 中寫入 5。

“這就是它在內存中的樣子:

二維數組

“順便說一句,對於二維數組,你也可以使用快速初始化:

// Lengths of months of the year in each quarter
int[][] months = { {31, 28, 31}, {30, 31, 30}, {31, 31, 30}, {31, 30, 31} };

“嗯……現在這很有趣。如果我們想像在第一個內括號中代表一個元素,下一個是第二個……那麼二維數組就像數組的數組?”

“你真是個聰明的學生!沒錯。第一個元素是一維數組{31, 28, 31},第二個是{30, 31, 30},依此類推。但我們稍後會在本課中回過頭來討論這個問題。在那之前,試著想一想作為具有行和列的表格的二維數組,在每個交叉點形成單元格。

“我心裡有數了。順便問一下,這些二維陣列是乾什麼用的?”

“程序員經常需要二維數組。如果仔細觀察,幾乎所有棋盤遊戲都是使用現成的二維數組實現的:國際象棋、西洋跳棋、井字棋、海戰等:”

海戰

“我明白了!象棋或者海戰的比賽場地,完全適合二維陣法!”

“是的,但你需要使用數字作為單元格坐標。不是‘pawn e2-e4’,而是‘pawn (5,2) -> (5,4)’。作為程序員,這對你來說會更容易。 “

排列數組中的元素:(x, y) 或 (y, x)

“創建二維數組會引發一個有趣的難題。當我們使用創建數組時,我們有一個‘兩5new int [2][5];’的表格還是‘兩列 5 行’?”

“換句話說,目前還不完全清楚我們是先指定寬度然後再指定高度……還是相反,先指定高度再指定寬度?”

“是的,這就是困境。而且沒有確定的答案。”

“該怎麼辦?”

“首先,重要的是要了解我們的二維數組實際上是如何存儲在內存中的。自然地,計算機內存中實際上並沒有任何表格:內存中的每個位置都有一個連續的數字地址:0、1、2, ...對我們來說,這是一個 2 × 5 的表格,但在內存中它只是 10 個單元格,僅此而已。沒有劃分行和列。”

“我明白了。那麼我們如何確定哪個尺寸在前——寬度還是高度?”

“讓我們考慮第一個選項。首先是寬度,然後是高度。”贊成這種方法的論據是這樣的:每個人在學校學習數學,他們都知道坐標對寫成'x'(即水平軸)然後是“y”(垂直維度)。這不僅僅是一個學校標準——它是一個普遍接受的數學標準。正如他們所說,你不能與數學爭論。”

「是嗎?好吧,既然打不過,那就先寬後高吧?」

“有一個支持‘先高後寬’的有趣論點。這個論點來自於二維數組的快速初始化。畢竟,如果我們想初始化我們的數組,那麼我們會這樣寫代碼:”

// Matrix of important data
int[][] matrix = { {1, 2, 3, 4, 5}, {1, 2, 3, 4, 5} };

“那對我們有什麼用呢?”

“你有沒有註意到什麼?如果我們有這個怎麼辦?

// Matrix of important data
int[][] matrix = {
  {1, 2, 3, 4, 5},
  {1, 2, 3, 4, 5}
};

“如果我們在代碼中逐行寫入我們的數據,那麼我們會得到一個包含 2 行和 5 列的表格。”

“現在我明白了。2是高度,5是寬度……那麼我們應該使用哪個選項呢?”

“由你來決定哪個更方便。最重要的是所有從事同一個項目的程序員都堅持同一種方法。”

“如果你在一個項目中工作,它的代碼有很多初始化的二維數組,那麼很可能那裡的一切都基於快速數據初始化,即你將擁有標準的‘高度 x 寬度’。

“如果你發現自己在一個涉及大量數學和坐標的項目中(例如,遊戲引擎),那麼代碼很可能會採用“寬 x 高”的方法。

二維數組是怎麼排列的

“現在,你還記得你在課程開始時注意到的二維數組的特殊性嗎?”

“對!就是二維陣法,其實就是陣法的陣法!”

“完全正確。”換句話說,如果在普通數組的情況下,數組變量存儲對存儲數組元素的容器的引用,那麼在二維數組的情況下,情況有點爆炸:二維-array 變量存儲對容器的引用,該容器存儲對一維數組的引用。最好只看一次,而不是嘗試解釋一百遍:”

二維數組是怎麼排列的

“在左邊,我們有一個二維數組變量,它存儲了對二維數組對象的引用。在中間一個二維數組對象,其單元格存儲一維數組,這是二維數組的行。在右邊你可以看到四個一維數組——我們的二維數組的行。這就是二維數組的實際工作方式。”

“太棒了!但它給了我們什麼?”

“由於‘容器的容器’存儲對‘行數組’的引用,我們可以非常快速、輕鬆地交換行。要獲得‘容器的容器’,您只需指定一個索引而不是兩個。示例:

int[][] data = new int[2][5];
int[] row1 = data[0];
int[] row2 = data[1];

“看下面的代碼。我們可以用它來交換行:”

// Matrix of important data
int[][] matrix = {
  {1, 2, 3, 4, 5},
  {5, 4, 3, 2, 1}
};

int[] tmp = matrix[0];
matrix[0] = matrix[1];
matrix[1] = tmp;
二維數組





matrix[0]存儲對第一行的引用。
我們交換參考。

結果,matrix數組看起來像這樣:
{
  {5, 4, 3, 2, 1},
  {1, 2, 3, 4, 5}
};

“明白了。就像交換任意兩個普通物品一樣。”

“確實如此。好吧,如果你引用二維數組的一個單元格,但你只在數組名稱後指定一個索引,那麼你指的是一個容器的容器,其單元格存儲對普通一-維數組。”

“一切似乎都合乎邏輯且清晰。感謝您的演講,Rishi!”

“不客氣。明智地付諸實踐。”