初識資料結構 - 最常被使用的物件容器?!

  • 233
  • 0
  • C#
  • 2020-06-09

初學程式不久,常聽大家說:「要精通寫程式,先要學會資料結構與演算法」。顯然地,這些重複性資訊讓我意會到 — 認識資料結構,是我的第一門大課。然而演算法關係到邏輯的思考,經考量目前功力不及,等日後有所體悟再跟大家介紹(笑)。

那麼,什麼是資料結構?白話來說,就是針對場景的需要、選擇正確的物件容器來存儲資料。What? 沒聽過物件容器這種東西!?

物件容器簡單來說,就是以下總類:Array陣列、List串列、Dictionary、Stack堆疊、Queue佇列.....等的總稱。它們都可以容納所有型別(包含自訂型別);以WindowsForm為例,button、textbox、label.....等控件都能放進物件容器裡,所以不要有只能包含基本型別的錯覺。

相信看到這邊,還是有人不了解,為何選擇對的物件容器來存儲資料攸關重要?以及為何這樣做能讓code看起來更清晰簡潔?

以下將以此範例為大家解說:

//若我想知道班上學生(共3名),此次段考每人的平均成績(國、英、數三科平均)為多少?
//輸出學生的姓名與平均成績、班級的科目平均
//限制:個別學生資料存儲成一筆

//以Array為例
Student[] stuArr = new Student[3];
Student stu1 = new Student() { Name = "Lucy", Chi = 87, Eng = 99, Mat = 88 };
Console.WriteLine($"{stu1.Name}的平均成績為{stu1.Avg}");//唯讀屬性stu1.Avg

Student stu2 = new Student() { Name = "Yuta", Chi = 78, Eng = 97, Mat = 89 };
Console.WriteLine($"{stu2.Name}的平均成績為{stu2.Avg}");

Student stu3 = new Student() { Name = "Pei", Chi = 89, Eng = 87, Mat = 85 };
Console.WriteLine($"{stu3.Name}的平均成績為{stu3.Avg}");

stuArr[0] = stu1;
stuArr[1] = stu2;
stuArr[2] = stu3;
double chiAvg = stuArr.Average(stu => stu.Chi);
double engAvg = stuArr.Average(stu => stu.Eng);
double matAvg = stuArr.Average(stu => stu.Mat);
//擴充方法stuList.Average(stu => stu.Chi)

Console.WriteLine(chiAvg);
Console.WriteLine(engAvg);
Console.WriteLine(matAvg);

輸出結果:Lucy的平均成績為91
Yuta的平均成績為88
Pei的平均成績為87
84.6666666666667
94.3333333333333
87.3333333333333

更正:Array也可以容納Student類別,寫法與List大同小異,因此也可以排除資料型別轉換的問題!(code已配合更正)

//以List為例
List<Student> stuList = new List<Student>();

Student stu1 = new Student() { Name = "Lucy", Chi = 87, Eng = 99, Mat = 88 };
Console.WriteLine($"{stu1.Name}的平均成績為{stu1.Avg}");//唯讀屬性stu1.Avg

Student stu2 = new Student() { Name = "Yuta", Chi = 78, Eng = 97, Mat = 89 };
Console.WriteLine($"{stu2.Name}的平均成績為{stu2.Avg}");

Student stu3 = new Student() { Name = "Pei", Chi = 89, Eng = 87, Mat = 85 };
Console.WriteLine($"{stu3.Name}的平均成績為{stu3.Avg}");

stuList.Add(stu1);
stuList.Add(stu2);
stuList.Add(stu3);
Console.WriteLine($"chiAvg={stuList.Average(stu => stu.Chi)}");
Console.WriteLine($"engAvg={stuList.Average(stu => stu.Eng)}");
Console.WriteLine($"matAvg={stuList.Average(stu => stu.Mat)}");
//擴充方法stuList.Average(stu => stu.Chi)

輸出結果:
Lucy的平均成績為91
Yuta的平均成績為88
Pei的平均成績為87
chiAvg=84.6666666666667
engAvg=94.3333333333333
matAvg=87.3333333333333
 

 class Student
    {
        public string Name { get; set; }
        public int Chi { get; set; }
        public int Eng { get; set; }
        public int Mat { get; set; }

        private int avg;

        public int Avg
        {
            get { return avg=(Chi+Eng+Mat)/3; }//唯讀屬性可以避免被篡改平均成績           
        }

    }

//在同一個專案裡,自訂Student型別,目的是建立一筆有意義的資訊。
//Student類別=>現實世界中學生物件的抽象化,而"抽象化後得到的多筆變數資訊",都關於個別學生的當前狀態。
//所以把相關資訊都存儲在同一個類別裡,就變成"一筆有意義的資訊",並且它關於個別學生物件的當前狀態。
//而不像散落各地的欄位或屬性,這就是有類別與無類別的差異。

//註:"抽象化後得到的多筆變數資訊":Name,Chi,Eng,Mat,Avg

以此例,選擇List為物件容器,你會發現List所容納的Student類別,內部包含了不盡相同的屬性型別;
List可以配合唯讀屬性、擴充方法,更快速地計算出平均成績。

結論:
初學時,我不太習慣選擇Array來存儲非基本型別的資料,但它其實就像List一樣,也可以容納自訂型別。
以此例來說他們的功能大同小異,並且Array也可配合擴充方法。


下篇將為大家介紹兩者的其他特點(優劣)。《比較:Array與List》

那麼,下篇見!

如有敘述錯誤,還請不吝嗇留言指教,thanks!