2013年8月1日 星期四

小山的 C# 教學-第22課-Array 陣列

本課簡介

寫程式偶爾會遇到需要儲存大量資料
而且資料類型又相同的情況
這個時候就輪到陣列登場了!

本課將介紹陣列的基本使用方式
以及解答一些關於陣列的常見問題


教學影片

注意:影片有高畫質 720P 的選項,可以看得更清楚喔!



重點提示

1. 陣列通常用來存放大量類型相同的資料,如 1000 個整數等等

2. 陣列的宣告方法如下,例如要宣告可以存放 40 個整數的陣列,名為 intArray:

int[] intArray = new int[40];
或是如果預先知道要存入的數值,也可以用下面的方式宣告:
(假設要預先存入 92, 83, 100)

int[] intArray = new int[] {92, 83, 100};
3. 如果要存取陣列內的資料,則需要打「陣列名稱 [ 編號 ]
例如要存取整數陣列 scores 編號 3 的資料:

scores[3] = 100; // 在編號 3 的位置存入 100
4. 陣列的編號是從 0 開始算起,因此如果陣列大小為 40 ,則可以用的編號是 0 ~ 39

5. 陣列一開始會將每個位置的資料填上預設值,預設值則跟每一格存放的資料型別有關。不同型別的預設值請參考「相關資訊連結」的「預設值表」

6. 如果使用了範圍外的編號,只有在程式執行時才會發現錯誤。因此要特別注意

7. 除了 int, double 這些基本型別,也可以建立物件陣列。只需要將型別名稱替換成 class 名稱即可,例如要宣告大小 20 的 Student class 的陣列可以寫:

Student[] students = new Student[20];
8. 陣列的大小是固定的,在宣告的時候就要決定

9. 陣列也是一種物件

補充

Class 的預設值

基本上,所有 Value Type 的變數的預設值都可以在相關連結的「預設值表」中找到。那麼 Reference Type(指向物件的變數) 的預設值又是甚麼?

所有 Reference Type 的預設值都是 null,也就是說,如果你一開始宣告了一個變數 object,然後還沒有用 new 來產生任何物件。那這個 object 裡面存的值就是 null。因此我們在寫程式的時候,常常可以使用下列這個判斷式來比對 Reference Type 的變數是否有指向任何物件。

if (object == null) // 如果 object 沒有指向任何物件,這個判斷式就會成立
另外,string 其實也是一個 class,所以它預設值也是 null。


Value Type 的陣列與 Reference Type 陣列的結構

在看這個條目之前,請先注意是否理解 Value Type 與 Reference Type 這兩種變數的差別。如果不了解的話,可以先去看「小山的 C# 教學-第16課-Value 與 Reference Type」。

在本課的教學中,我們了解到陣列儲存整數的方法是像下面這張圖。


而我們也學過,當我們建立物件的時候,物件的本體是放在一個名為 Heap(堆疊) 的物件海。然後我們物件的變數也只有紀錄物件本體的位置的而已,這些變數就稱為 Reference Type 的變數。

那這些 Reference Type 的陣列實際上的結構是長甚麼樣子呢?實際上,Reference Type 的陣列裡面所存的,只是一群「指向物件本體」的位址而已。如果我們有一個 class Student,然後我們建立了 Student 的陣列。再用下列的程式碼一一地建立物件,並利用陣列來儲存。

Student[] students = new Student[20]; students[0] = new Student(10001, 小山); students[1] = new Student(10002, 小羊); students[2] = new Student(10003, 元正); students[3] = new Student(10004, 高光); ....
那這些東西實際儲存的狀態就會像下面這張圖(箭頭代表我們可以透過位址去找到被指向的物件)


動態陣列

本課教學提到陣列是固定大小的,而且必須要在宣告時就決定大小。事實上,也有不需要事先指定大小,並且可以動態改變大小的陣列。

在 System.Collectioins 這個 namespace 下有一個 class 叫做 ArrayList。它本身的結構與陣列很類似,但是具有自行動態調整大小的特性。當然使用方法上會有些不同。關於這部分的介紹有網友製作了簡單的說明,有興趣的可以點進去參考。


相關資訊連結

微軟官方(MSDN) 對於陣列的簡介

http://msdn.microsoft.com/zh-tw/library/9b9dty7d%28v=vs.80%29.aspx

MSDN 的 Array class 參考資料

http://msdn.microsoft.com/en-us/library/system.array.aspx

C# Value Type 的預設值表

http://msdn.microsoft.com/zh-tw/library/83fhsxwc%28v=vs.80%29.aspx

某位網友對於 ArrayList 使用方法的簡單說明
《[C#.NET][VB.NET] 一般集合 - ArrayList 類別 排序》

http://www.dotblogs.com.tw/yc421206/archive/2009/01/21/6902.aspx

26 則留言:

  1. 小山您好:
    近期跟著您的腳步學習C#
    在這裡我有一個比較複雜的程式想請教您 不是請教您他的內容
    而是程式架構部份

    不知是否願意以讓我已寄信的方式向您詢問
    我的信箱 redonizuka@gmail.com

    回覆刪除
    回覆
    1. 沒問題,請寄信到以下信箱
      sam123456777@gmail.com

      刪除
  2. 小山您好:

    有一個程式碼不太懂意思想請教您

    if ((x[i] & 0x80) == 0x80) 上面有個FOR迴圈就沒寫出來了

    這句話的意思是我要進入if的話 我x的第i項要是 0x80和我&後面的0x80都要是0x80才能進入嗎

    那我有問題是&後面就己經是0x80了 為何還要&判斷

    回覆刪除
    回覆
    1. 這有點牽涉到邏輯設計的概念
      在這之前,我先解釋一下「&」的意義

      在 C# 中,「&&」、「&」分別有不同意義
      注意:以下說的「true」代表「成立」、「false」代表「不成立」

      「&&」代表是,如果符號前後都是 true,就是 true,如果前後有一個是 false,那整個式子就是 false

      「&」比較特別,我們稱之為「位元運算子 Bitwise operator」
      他必須要配合數字或者可以存數字的變數使用
      首先必須要把符號前後的數字拆成二進位
      然後進行「AND 運算」
      你可以參考下面那個網址左上角的表,如果看不懂可以再問我
      http://www.csie.nctu.edu.tw/~skyang/figures/bitwiseops.gif

      舉例來說:
      假設 A = 00101, B = 11001

      00101 <-- 這是 A
      11001 <-- 這是 B
      -----
      00001 <-- 結果

      因此 A & B = 00001

      那閣下所問的 0x80 是八進位表示法
      所以必須先把 0x80 轉換為二進位,也就是 10000000(二進位)
      然後再跟 x[i] 進行上面說的 AND 運算
      最後在看結果是否等於 0x80

      我相信這應該不太好懂
      如果有問題歡迎再問~

      刪除
    2. 如果說同樣長度的話我就懂
      比方說 A=00011 B=11010 在去& 在去比較

      但現在他是x[i] 但i隨著FOR為 1 2 3
      假如他現在是x[1]的話 要怎麼跟0x80去&

      刪除
    3. 首先,如果長度不一樣的話
      會藉由補 0 將短的先變得跟長的一樣長
      例如:

      a = 1001
      b = 1110001

      如果做 a & b
      就會先把 a 變成 00001001 (左邊補四個 0)
      然後再做 & 運算

      如果你的 x[i] 是 int 陣列
      那 x[1] 裡面自然就會存著一個 int 數值
      所以就是拿那個 int 數值去跟 0x80 做 &

      刪除
  3. 另一個新問題
    針對位元組的部份
    byte[] a = new byte[b]
    這個意思是說我將b以位元形式放到a位元矩陣 這樣嗎

    位元是不是無法以Message.show show出來

    回覆刪除
    回覆
    1. 首先,在 C# 中 byte 跟 int 都是代表整數
      唯一的差別是在 byte 只能存 0 到 255 的整數
      (因為 byte 儲存空間是 1 byte = 8 bit = 255 種組合)
      int 則可以存 -2,147,483,648 至 2,147,483,647
      (int 儲存空間是 4 byte = 32 bit = 2 的 32 次方種組合)

      就你的例子來看
      假設 b = 20
      byte[] a = new byte[b];
      只是代表建立一個可以「分別」存放 20 個 byte 數值的陣列而已

      因為 byte 也是一種整數
      所以同樣也能夠顯示出來
      假設你宣告 byte a = 10;
      只要打 MessageBox.Show("" + a); 就可以顯示了

      如果你指的是用「二進位」的方式顯示 a
      就打 MessageBox.Show(Convert.ToString(a, 2));

      刪除
    2. 打錯了,byte 是 2 的八次方 => 256 種組合

      刪除
  4. 感謝您 目前有詢問您的問題全都了解了

    另一新問題

    a[0] <<= 1 這個是位移運算子吧 就是將我的位元往左移一個單位

    回覆刪除
  5. 小山老師 我想請教一下
    例如說
    student student1 = new student(); //第一個學生
    如果有60個學生
    並且使用for編寫
    請問student1~student60 數字與文字組成的新名稱該如何組合?

    for(int i = 0; i < 60; i++)
    {
    student ????? = new student();
    }

    ?????這邊該如何編寫?

    回覆刪除
  6. 作者已經移除這則留言。

    回覆刪除
  7. 你這個問題正契合了陣列存在的目的
    陣列就是為了要儲存「大量且同型別」的資料而存在的
    因此你這個問題應該要用陣列編寫如下:

    Student[] students = new Student[60]; // 先宣告一個可以存放 60 筆資料的陣列

    for (int i = 0; i < 60; i++)
    students[i] = new Student(); // 一個一個地建立學生物件,注意左側的 i 會隨 for 改變

    回覆刪除
  8. 您好,請問一下

    請建立c#應用程式宣告5個元素的一為陣列後,使用亂數類別來產生陣列的元素值,其範圍是1~200的整數,然後將陣列內容排序後,顯示在標籤控制項。

    這要怎麼做?我在標籤上都只能顯示一個數值,要如何同時顯示5個數值且不重複?

    回覆刪除
    回覆
    1. 我有不太懂顯示一個數值的意思,是指你有一個標籤(Label)
      然後你想一次塞入五個數值嗎?
      像是 "1 2 5 8 10" 這樣嗎?

      關於不重複的部分
      我大概想到兩種做法:

      1. 你有建立陣列(Array)對吧,那當你亂數產生一個數字的時候
      先檢查一下陣列裡是否已經存在一樣的數值
      如果有的話,就重新產生一個新的數值
      如果沒有,就把這個數值存進陣列
      一直到塞滿五個為止

      2. 你可以把 1~200 切成五段
      分別是 1~40, 41~80, 81~120, 121~160, 161~200
      然後每一段各自產生一個數字
      這樣就能夠保證不重複了

      作法一的缺點是,每次都要搜尋陣列很耗時間
      但是你只需要產生五個,所以其實也還好

      作法二的缺點是,產生出來的數字並非真的亂數
      同一個區間的數字無法同時出現

      如果想要用上述的做法,可能需要斟酌一下

      刪除
    2. 小山你好 , 我剛好跟joe有相同的問題
      我問題是不知道怎麼去『一次和陣列裡全部的數字做比較』

      int random_number;
      int[] NumberArr = new int[2];

      for (int a = 0; a< 2; a ++)
      {
      bool key = false ;
      random_number = Random.Range (1, 3);


      if(key == false)
      {
      for(int d = 0; d<NumberArr.Length ; d++)
      {
      if(NumberArr[d] == random_number)
      {
      random_number = Random.Range (1, 3);
      }
      }

      }

      NumberArr [a] = random_number;
      key = true ;

      我發現我這樣比較NumberArr[1]的時候會有機會出現跟Number[0]的數字(重複)
      我在猜是因為一開始NumberArr[1]也沒設數值的關係所以以沒法判斷
      請教一下要怎麼跟全部的數字作判斷呢? (假設有10個好了)

      刪除
    3. 一般來說,在不借助其他資源的情況下
      應該是每次得到一個數字之後
      就要使用一個 for 迴圈依序檢查陣列中的所有數字

      不過我這裡提供一個更快速的解法:
      在 C# 中有一種資料結構叫做 HashSet
      所謂資料結構就是一種可以幫你有效率保存大量資料的物件
      Set 就是一種資料結構
      Set 的特點在於裡面所有的值都「不會重複」
      HashSet 的則是一種查詢速度很快的 Set

      所以如果你今天想要得到一群不重複的亂數 (假設 10 個)
      只要這樣寫就可以了:

      HashSet numbers = new HashSet();
      while (numbers.Count < 10) {
      numbers.add(Random.Next());
      }

      刪除
  9. 你好,想請問小山大大
    (1) 第一天存10元,第二天存20元,第三天存30元,依此類推。
    (2) 當存款達到5000元後,再回到(1)的存錢方式,繼續存錢。
    (3) 請問小新存錢到第幾天,他的存款會達到20000元?
    (4) 若依此方式繼續存下去,到第50天時,小新會有多少存款?

    我知道這麼不知廉恥的問作業題目很不好
    但老師教的我實在無法理解

    (1) 第一天存10元,第二天存20元,第三天存30元,依此類推。
    這題是用While就可解決,但是要讓它停在5000後再重複執行,這我就完全不能明白了
    是使用IF跟FOR搭配陣列去解題嗎?
    可煩請小山大大幫忙解惑嗎? 感謝你

    回覆刪除
    回覆
    1. Hello, 我想先確認一下
      (2) 的 5000 是指說「單次存下去的錢 5000」
      還是「存款總共有 5000」?

      刪除
    2. 用兩個while+一個if即可解決

      public int cash=0 , day=0, reset, total, cash1;

      while (cash < 5000)
      {
      day++;
      cash += 10 * day;
      }
      while (cash <= 20000)
      {
      reset++;
      cash += 10 * reset;

      total = day + reset;

      if (total == 50)
      {
      cash1 = cash;
      }
      }

      MessageBox.Show("到第 " + total + " 天,存款達到20000元\r\n" + "第50天時,有 " + cash1 + "存款");

      刪除
  10. 作者已經移除這則留言。

    回覆刪除
  11. 請問前兩則是不是寫錯了@@
    int intArray = new int[40];
    int intArray = new int[] {92, 83, 100};

    前面應該是int[] intArray ?
    (還是這是為了測試大家有沒有專心看影片,故意打錯的@@?)

    回覆刪除
    回覆
    1. 抱歉,應該是我寫錯了XD
      感謝指正

      刪除
  12. 小山老師 我想請教一下 若一共有24個物件 分三段選 然後要判斷使用者是否依照順序選出正確的物件七個 12個 五個 就判斷對還錯 對就會顯示一張圖片 要寫c#要怎麼寫

    回覆刪除
  13. 感謝小山老師的指導,老師做的教學很棒,潛顯易懂,讓我這個第一次接觸語言的也能聽懂。期待新作品 謝謝

    回覆刪除