Tại sao lại có kiểu tập hợp Giả sử ta cần nhập 1 danh sách sinh viên để xử lí Nếu biết trước số sinh viên, ta có thể tạo 1 mảng để chứa danh sách này Nếu chưa biết trước số sinh v
Trang 1Collections & Generics
Kiểu tập hợp và kiểu tổng quát
Trang 2Tại sao lại
có kiểu tập
hợp
Giả sử ta cần nhập 1 danh sách sinh viên để xử lí
Nếu biết trước số sinh viên, ta có thể tạo 1 mảng để chứa danh sách này
Nếu chưa biết trước số sinh viên, ta có thể tạo 1 mảng
đủ lớn rồi giới hạn số sinh viên được nhập
Hai cách trên có vẻ hợp lí, nhưng nếu ta muốn nhập thêm 1 sinh viên khi mảng đã đầy thì không thể làm được
Từ đó nảy sinh yêu cầu phải có 1 kiểu dữ liệu nào đó
có số phần tử thay đổi được, đó chính là kiểu tập hợp (Collections)
Trang 3Kiểu
Collection
NET cung cấp một số class để giải quyết sự giới hạn phần tử của mảng, đó là các lớp collection Các lớp này được thiết kế để có số phần tử thay đổi được, nó cung cấp các phương thức để chèn và xóa phần tử trong nó
Có 2 loại kiểu tập hợp:
Kiểu tập hợp không tổng quát (nằm trong các namespace System.Collections,
System.Collections.Specialized)
Kiểu tập hợp tổng quát (nằm trong namespace System.Collections.Generics)
Trang 4Kiểu tập
hợp không
tổng quát
Khi nền tảng NET mới ra đời, các lập trình viên thường dùng các kiểu tập hợp không tổng quát, nằm trong namespace System.Collections (sau này từ
phiên bản NET 2.0, các kiểu tập hợp này đều được tổng quát hóa và nằm trong namespace
System.Collections.Generics)
Các phần tử trong kiểu tập hợp không tổng quát đều
có kiểu là object
Trang 5Namespace
System.Collecti
ons
Interface Ý nghĩa
ICollection Định nghĩa các thông số tổng quát của
bất kì tập hợp không tổng quát nào ICloneable Cho phép đối tượng trả về 1 đối tượng là
bản sao của nó IDictionary Cho phép đối tượng nongeneric biểu
diễn nội dun của nó bằng cặp key-value IEnumerable Cho phép trả về 1 đối tượng hiện thực
giao diện IEnumerator IEnumerator Cho phép từ khóa foreach duyệt các
phần tử của nó IList Cho phép thêm, xóa, truy cập phần tử
thông qua chỉ số của nó trong dãy
Trang 6Namespace
System.Collecti
ons
Class Interface hỗ trợ
(không kể ICollection)
Ý nghĩa
ArrayList IList, IEnumerable,
ICloneable Giống như Array nhưng số phần tử của nó có thể tăng
lên nếu cần Hashtabl
e IDictionary, IEnumerable,
ICloneable
Tập hợp các cặp giá trị key-value, truy cập value theo key
Queue IEnumerable,
ICloneable Tập hợp các đối tượng theo kiểu hàng đợi (in
first-out, FIFO) SortedLi
st IDictionary, IEnumerable,
ICloneable
Tập hợp các cặp giá trị key-value, được sắp xếp thứ tự theo key, có thể truy xuất theo key hoặc theo value Stack IEnumerable,
ICloneable Tập hợp các đối tượng theo kiểu ngăn xếp (last-in
first-out LIFO), có các phương thức push và pop
Trang 7Ví dụ về
ArrayList
ArrayList AL = new ArrayList(); AL.Add(“Hello”);
AL.Add(“World”);
AL.Add(“!”);
foreach(string s in AL) {
Console.WriteLine(“{0} ”, s); }
// Output // Hello World !
Trang 8Các vấn đề
của kiểu tập
hợp không
tổng quát
Vấn đề về hiệu năng: Khi bạn cần thao tác với các dữ liệu số (hoặc value type), chương trình phải tốn thêm
bộ nhớ để chuyển kiểu value sang kiểu reference lưu trữ trên heap, khi chuyển lại về kiểu value, ta lại tốn them bộ nhớ để lưu nó trên stack Những điều trên ảnh hưởng rất lớn đến hiệu năng của chương trình
Vấn đề về an toàn kiểu: Vì kiểu dữ liệu của tập hợp không tổng quát là object nên nó có thể chứa bất kì đối tượng nào Khi ta tạo 1 danh sách những chiếc xe,
ta phải đảm bảo rằng ta chỉ thêm các đối tượng xe vào trong danh sách, nếu ta thêm các đối tượng khác, việc biên dịch vẫn thành công nhưng sẽ gây lỗi lúc
chạy
Trang 9Cái nhìn
đầu tiên về
kiểu tổng
quát
List<Car> carList = new List<Car>(); carList.Add(new Car(“Red”));
carList.Add(new Car(“Green”));
List<int> intList = new List<int>(); intList.Add(1);
// Lệnh sau sẽ gây lỗ0i // intList.Add(new Car(“Red”));
Trang 10Kiểu dữ liệu
trừu tượng
public class List<T> : IList<T>, ICollection<T>,
IList, ICollection, IReadOnlyList<T>,
IReadOnlyCollection<T>, IEnumerable<T>,
IEnumerable
{ public void Add(T item);
public int BinarySearch(T item);
public bool Contains(T item);
public bool Remove(T item);
public T[] ToArray();
}
Trang 11Kiểu dữ liệu
trừu tượng
public class List<int> : IList<int>,
ICollection<int>, IList, ICollection,
IReadOnlyList<int>, IReadOnlyCollection<int>,
IEnumerable<int>, IEnumerable
{ public void Add(int item);
public int BinarySearch(int item);
public bool Contains(int item);
public bool Remove(int item);
public int[] ToArray();
}
Trang 12Kiểu dữ liệu
trừu tượng
Kiểu dữ liệu cũng dùng trong các thành viên generic:
Array.Sort<int>(intList);
Array.Sort<Car>(carList);
Giao diện generic
public interface IComparable<T>
{ int CompareTo(T obj);
}
Trang 13Namespace
System.Collecti
ons.Generic
ICollection<T> Định nghĩa các thông số tổng quát của
bất kì tập hợp tổng quát nào IComparer<T> Cho phép đối tượng so sánh với đối
tượng khác IDictionary<TK
ey, TValue> Cho phép đối tượng generic biểu diễn nội dun của nó bằng cặp key-value IEnumerable<T
> Cho phép trả về 1 đối tượng hiện thực giao diện IEnumerator IEnumerator<T
> Cho phép từ khóa foreach duyệt các phần tử của nó IList<T> Cho phép thêm, xóa, truy cập phần tử
thông qua chỉ số của nó trong dãy
Trang 14Namespace
System.Collecti
ons.Generic
Class Interface hỗ trợ Ý nghĩa
List<T> IList<T>,
IEnumerable<T>, ICollection<T>
Giống ArrayList
Dictionary
<TKey, TValue>
IDictionary<TKey, TValue>,
IEnumerable<T>
Giống Hashtable
Queue<T> IEnumerable<T>,
ICollection Giống Queue LinkedList<T
> IEnumerable<T>, ICollection<T> Danh sách móc nối 2 chiều Stack<T> IEnumerable<T>,
ICollection Giống Stack SortedDiction
ary
<TKey, TValue>
ICollection<T>, IDictionary<TKey, TValue>,
IEnumerable<T>
Giống Dictionary + các phần tử được sắp xếp
Trang 15Khởi tạo đối
tượng
collection
Cú pháp khởi tạo đối tượng này chỉ áp dụng cho các class có phương thức Add
List<int> intList = new List<int> { 1, 2, 4, 5 };
ArrayList myList = new ArrayList { 3, 6, 4,
7 };
Khác với mảng, khi khởi tạo đối tượng collection, ta phải sử dụng từ khóa new
int[] arr = { 0, 1, 3, 7 };
Trang 16Một vài
class
generic
collection
List<T>:
Add(T): thêm 1 phần tử vào cuối danh sách
Remove(T): xóa 1 phần tử xuất hiện đầu tiên trong danh sách
Queue<T>:
Dequeue(): trả về phần tử đầu tiên của hàng đợi và xóa nó
Enqueue(T): thêm phần tử vào cuối hàng đợi
Peek(): lấy phần tử đầu tiên của hàng đợi nhưng không xóa nó
Stack<T>:
Push(T): thêm phần tử vào đầu ngăn xếp
Pop(): trả về phần tử đầu tiên của ngăn xếp và xóa nó
Peek(): lấy phần tử đầu tiên của ngăn xếp nhưng không xóa nó
Trang 17Tự tạo
phương
thức
generic
Ví dụ ta muốn đổi chỗ 2 đối tượng cùng 1 kiểu, nếu viết theo cách thông thường, ta phải viết từng phương thức cho từng kiểu dữ liệu Với generic, ta chỉ cần viết
1 phương thức
static void DoiCho<T>(ref T a, ref T b) {
T temp = a;
a = b;
b = temp;
}
Khi gọi phương thức này, tùy thuộc vào kiểu của đổi tượng mà ta thay đổi kiểu của T:
DoiCho<int>(ref a, ref b);
DoiCho<Car>(ref car1, ref car2);
Trang 18Tự tạo class
hoặc struct
generic
public class Point<T>
{ public T X { get; set; } public T Y { get; set; } public void Draw()
{ Console.WriteLine("{0} {1}", X, Y); }
public Point() { } public Point(T xVal, T yVal) {
X = xVal;
Y = yVal;
} }
Tương tự như các class
hoặc struct bình thường,
chỉ khác là những kiểu dữ
liệu được tổng quát hóa
thành kí hiệu T
Trang 19Từ khóa
default
Từ khóa default dùng để lấy giá trị mặc định của 1 kiểu dữ liệu
Nếu đó là kiểu value, nó sẽ trả về 0, hoặc false…
Nếu là kiểu reference, sẽ trả về null
public void ResetData() {
X = Y = default(T);
}
Trang 20Giới hạn
kiểu dữ liệu
trong
generic
where T : struct Kiểu T là kiểu value (struct, int,
double…) where T : class Kiểu T là kiểu reference where T : new() Kiểu T phải có hàm khởi tạo mặc định
(không tham số) where T : tên
class Kiểu T phải kế thừa (trực tiếp hoặc gián tiếp) từ class chỉ định where T : tên
interface Kiểu T phải hiện thực giao diện chỉ định
Có thể sử dụng cùng lúc nhiều loại mã giới hạn public class MyGeneric1<T> where T : struct public class MyGeneric2<T> where T : class, new() public class MyGeneric3<T> where T : struct,
IPoint
public void Swap<T>(ref T a, ref T b) where T : class