Collections và Ngoại lệ trong Dart
- 07-06-2024
- Toanngo92
- 0 Comments
Mục lục
Collections trong Dart và cách sử dụng của chúng
Collections được sử dụng để lưu trữ, truy xuất và truyền tải dữ liệu lớn. Chúng đại diện cho các mục dữ liệu tạo thành một cụm tự nhiên, chẳng hạn như một thư mục (bao gồm nhiều thư mục), hoặc một danh bạ (liên kết tên với số điện thoại).
Collections là một thực thể đại diện cho một nhóm các đối tượng gọi là phần tử. Thư viện dart hỗ trợ cho các Collections trong Dart và có thể được thêm vào một tập lệnh Dart bằng cách sử dụng từ khóa import
.
Các cấu trúc dữ liệu tối ưu cao được hình thành từ các Collections, thực chất là nhóm các lớp và giao diện. Nếu lựa chọn Collections một cách tốt, có thể giảm công sức lập trình và cải thiện hiệu suất mã.
Các loại Collections trong Dart
Dưới đây là các loại Collections trong Dart:
List
List là một nhóm các đối tượng có thứ tự. Một lớp List
cho phép tạo và chỉnh sửa List
được cung cấp bởi thư viện Dart
List có thể được chia thành hai loại:
- Fixed Length List: Loại List này không thể thay đổi hoặc chỉnh sửa tại thời điểm chạy. Kích thước của List được xác định trong quá trình khởi tạo và không thể thay đổi sau đó.
Hãy xem đoạn mã bên dưới đây:
void main() {
List list1 = List.filled(2, 0);
list1[0] = 50;
list1[1] = 100;
print(list1);
}
Trong Đoạn mã trên, một thể hiện của List tên là list1
với kích thước 2 được tạo bằng phương thức List.filled
, trong đó độ dài cố định và các giá trị ban đầu trong danh sách cần được chỉ định. Nó sau đó được khởi tạo với hai giá trị. [0]
và [1]
là các vị trí chỉ mục. Số nguyên 50 được thêm vào chỉ mục 0 và số nguyên 100 được thêm vào chỉ mục 1. Danh sách sau đó được in ra bằng hàm print()
.
Hình bên dưới trình bày đầu ra của Đoạn mã bên trên.
2. Growable Length List: Loại danh sách này có thể thay đổi hoặc chỉnh sửa tại thời điểm chạy. Việc xác định kích thước của danh sách không cần thiết khi khởi tạo một danh sách mới cho một biến. Điều này có nghĩa là bất kỳ số lượng phần tử nào cũng có thể được thêm vào hoặc xóa khỏi danh sách.
Hãy xem đoạn mã bên dưới:
void main() {
var newlist = [10, 20, 30, 40, 50];
newlist.add(60);
print(newlist);
}
Trong Đoạn mã trên, một biến newlist
được khai báo. Các phần tử được thêm vào danh sách trong quá trình khai báo. Hơn nữa, số nguyên 60 được thêm vào bằng cách sử dụng phương thức add()
cho List trong Dart. Danh sách sau đó được in ra bằng hàm print()
.
Hình bên dưới trình bày đầu ra của Đoạn mã trên.
Set
Set là một bộ sưu tập các giá trị duy nhất và không có thứ tự của cùng một kiểu dữ liệu. Các giá trị này chỉ xuất hiện một lần trong Set. Thư viện Dart cung cấp lớp Set. Việc tạo và chỉnh sửa Set phải được thực hiện bằng cách sử dụng thư viện Dart.
Hãy xem đoạn mã bên dưới:
void main() {
Set s = new Set();
s.add(10);
s.add(20);
s.add(30);
s.add(40);
s.add(40);
print(s);
}
Trong Đoạn mã bên dưới, một thể hiện của lớp Set được khai báo với tên biến s
. Các phần tử sau đó được thêm vào bằng phương thức add()
của Set. Một nỗ lực được thực hiện để thêm giá trị 40 hai lần vào cùng một Set và in nó ra. Vì Set là một bộ sưu tập các phần tử duy nhất, một số sẽ chỉ được thêm vào nếu nó chưa có trong Set. Do đó, số 40 sẽ không được thêm vào lần thứ hai vì nó đã có mặt. Vì chỉ dữ liệu duy nhất được phép trong Set, nên nó sẽ tránh in ra bất kỳ phần tử trùng lặp nào và do đó, số 40 không xuất hiện lần thứ hai.
Hình bên dưới trình bày đầu ra của Đoạn mã bên trên.
Map
Map là một bộ sưu tập động với cặp khóa-giá trị đơn giản. Map hỗ trợ tất cả các loại dữ liệu và mỗi khóa bên trong Map phải tồn tại sao cho chúng là duy nhất hoặc khác biệt với nhau.
Dưới đây là hai cách riêng biệt để khai báo một Map:
Map Literals: Map được khai báo bằng cách bao gồm các cặp khóa-giá trị bên trong một cặp dấu ngoặc nhọn { }
.
void main() {
var userdetails = {'email': 'jack@gmail.com', 'password': '12345'};
print(userdetails);
}
Trong Đoạn mã trên, một biến userdetails
được khởi tạo như một Map và các khóa email
và password
được thêm vào. Giá trị jack@gmail.com
được thêm vào khóa email
và giá trị 12345
được thêm vào khóa password
. Map sau đó được in ra bằng hàm print()
.
Hình bên dưới trình bày đầu ra của Đoạn mã bên trên.
Map Constructor: Một thể hiện của Map được khai báo và sau đó được khởi tạo. Trong một cặp khóa-giá trị, khóa được thêm vào Map bằng cách sử dụng dấu ngoặc và giá trị được thêm vào khóa.
void main() {
var details = new Map();
details['username'] = 'ron';
details['password'] = '12345';
print(details);
}
Trong Đoạn mã trên, một biến details
được khai báo như một Map với hàm tạo Map. Map sau đó được khởi tạo với một cặp khóa-giá trị.
Hình bên dưới trình bày đầu ra của Đoạn mã.
HashSet
HashSet là một bảng băm có triển khai Set không có thứ tự. Nếu giá trị x được chèn trước giá trị y trong HashSet, có thể khi duyệt các phần tử sẽ lấy được giá trị y trước giá trị x. HashSet được cung cấp bởi thư viện Dart trong Dart.
import 'dart:collection';
void main() {
Set numbers = new HashSet();
numbers.add(10);
numbers.add(20);
numbers.add(30);
numbers.add(40);
numbers.add(50);
print(numbers);
}
Trong Đoạn mã trên, một biến numbers
được khởi tạo như một HashSet. Các phần tử được chèn vào HashSet theo thứ tự 10, 20, 30, 40 và 50. Sau đó, HashSet được in ra bằng hàm print()
. Đầu ra sẽ không theo thứ tự các phần tử được thêm vào vì các phần tử của HashSet được sắp xếp theo hashCode.
Hình bên dưới trình bày đầu ra của Đoạn mã trên.
HashMap
HashMap là một triển khai Map theo bảng băm không có thứ tự. Nếu khóa x được chèn trước khóa y trong HashMap, có thể khi duyệt các phần tử sẽ lấy được khóa y trước khóa x. HashMap được cung cấp bởi thư viện Dart trong ngôn ngữ lập trình Dart.
import 'dart:collection';
void main() {
Map details = new HashMap();
details['name'] = 'dan';
details['email'] = 'dan@gmail.com';
details['number'] = 'xxxxxxxxx';
print(details);
}
Trong Đoạn mã trên, một biến details
được khởi tạo như một HashMap. Các khóa được chèn vào HashMap theo thứ tự name
, email
, và number
. Sau đó, HashMap được in ra bằng hàm print()
.
Hình bên dưới trình bày đầu ra của Đoạn mã trên.
LinkedList
LinkedList là một cấu trúc dữ liệu tuần tự có tính chất tuyến tính. Các phần tử mở rộng từ lớp LinkedListEntry có thể được đưa vào LinkedList. Mặc dù tên gọi bao gồm List, lớp này không triển khai giao diện List. Nó là đơn hướng và có nhiều ưu điểm so với danh sách Dart. Một số trong những ưu điểm này bao gồm lưu trữ không liên tục trong bộ nhớ, chèn và loại bỏ nhanh hơn, và hiệu suất tốt hơn.
import 'dart:collection';
class Items extends LinkedListEntry<Items> {
final int id;
final String name;
Items(this.id, this.name);
@override
String toString() {
return '$id: $name';
}
}
void main() {
final linkedList = LinkedList<Items>();
linkedList.addAll([
Items(1, 'jon'),
Items(2, 'natalia'),
Items(3, 'dina')
]);
print(linkedList);
}
Trong Đoạn mã trên, lớp Items
được tạo bằng cách mở rộng lớp LinkedListEntry
. Một biến số nguyên id
và một biến chuỗi name
đã được khai báo trong lớp Items
. Ngoài ra, một phương thức toString()
cũng được định nghĩa trong lớp để trả về id
và name
dưới dạng chuỗi. Trong hàm main()
, một thể hiện của LinkedList được khai báo và kiểu hóa về lớp Items
. Sau đó, các phần tử được thêm vào LinkedList bằng phương thức addAll()
từ lớp Collection
. LinkedList sau đó được in ra bằng hàm print()
.
Hình bên dưới trình bày đầu ra của Đoạn mã trên.
LinkedHashMap
LinkedHashMap thực thi dữ liệu dựa trên thứ tự sắp xếp, tương tự như thứ tự chèn. Nếu một khóa x được thêm vào trước và sau đó, một khóa y được thêm vào, có thể nhận được giá trị x trước khi duyệt các phần tử. LinkedHashMap có thể lưu trữ một khóa null và nhiều giá trị null.
import 'dart:collection';
void main() {
var lh = new LinkedHashMap();
lh['1'] = 'alice';
lh['2'] = 'bob';
lh['3'] = 'cindy';
lh['4'] = 'alex';
print(lh);
}
Trong Đoạn mã trên, một biến lh
được khởi tạo như một LinkedHashMap. Các khóa và giá trị được thêm vào LinkedHashMap theo thứ tự 1, 2, 3 và 4. Sau đó, LinkedHashMap được in ra bằng hàm print()
.
Hình bên dưới trình bày đầu ra của Đoạn mã.
LinkedHashSet
LinkedHashSet thực thi dữ liệu theo thứ tự chèn. Nếu một giá trị x được thêm vào trước và sau đó, giá trị y được chèn vào, giá trị x sẽ được nhận trước giá trị y khi duyệt các phần tử. LinkedHashSet chỉ có thể lưu trữ một giá trị null.
import 'dart:collection';
void main() {
var lhs = new LinkedHashSet();
lhs.add('dan');
lhs.add('rony');
lhs.add('sam');
print(lhs);
}
Trong Đoạn mã trên, một biến lhs
được khởi tạo như một LinkedHashSet. Các phần tử được thêm vào LinkedHashSet theo thứ tự dan
, rony
, và sam
. Sau đó, LinkedHashSet được in ra bằng hàm print()
.
Hình bên dưới trình bày đầu ra của Đoạn mã.
Nested Collections
Một Collection của Collection được gọi là Nested Collection. Một cách hiểu khác của khái niệm này là mảng đa chiều, bao gồm các hàng và cột. Một nested collection có thể được hiểu như là một mảng đa chiều.
Điều này có thể được triển khai trong Dart bằng cách sử dụng List, Set, Map, HashMap, HashSet và LinkedList. Trong Nested Collections, có thể triển khai không giới hạn các hàng và cột, do đó rất hữu ích cho việc tạo ra các ma trận. Đoạn mã bên dưới minh họa một ví dụ.
import 'dart:collection';
void main() {
List ll = List.generate(2, (_) => List.generate(2, (_) => 0));
print(ll);
}
Trong Đoạn mã trên, đối tượng List có tên ll
được khởi tạo với hai hàng và hai cột bằng phương thức generate()
của lớp List. Kết quả là một collection hai chiều với tất cả các phần tử được khởi tạo bằng giá trị 0. Sau đó, nó được in ra bằng hàm print()
.
Hình bên dưới trình bày đầu ra của Đoạn mã.
import 'dart:collection';
void main() {
List ll = List.generate(3, (_) => List.generate(3, (_) => 0));
print(ll);
}
Trong Đoạn mã trên, đối tượng List ll
được tạo và khởi tạo với ba hàng và ba cột bằng phương thức generate()
của lớp List, tạo ra một collection ba chiều với tất cả các phần tử được khởi tạo bằng giá trị 0. Sau đó, nó được in ra bằng hàm print()
.
Hình bên dưới trình bày đầu ra trong terminal khi thực hiện chương trình trong Đoạn mã trên.
Collection-if
Collection của các đối tượng có thể được kiểm tra và điều chỉnh bằng cách sử dụng các điều kiện if. Điều này thường được sử dụng khi cần sửa đổi của bộ sưu tập dựa trên các điều kiện cụ thể.
import 'dart:collection';
void main() {
List l1 = [];
l1.add(10);
l1.add(20);
l1.add(30);
l1.add(40);
print(l1[0]);
if (l1[0] < l1[1]) {
l1[0] = l1[0] + 10;
}
print(l1[0]);
}
Trong Đoạn mã trên, đối tượng List l1
được khởi tạo và sau đó, bốn giá trị số nguyên 10, 20, 30 và 40 được thêm vào. Phần tử thứ 0 của danh sách được in ra trước và sau điều kiện if. Trong điều kiện if, phần tử thứ 0 và thứ nhất được so sánh để xem liệu l1[0]
có nhỏ hơn l1[1]
không. Vì điều kiện là đúng, l1[0]
được tăng thêm 10.
Hình bên dưới trình bày đầu ra của Đoạn mã.
Collection-for
Đây là một bộ sưu tập các đối tượng có thể được lặp lại. Các phần tử hoặc bộ sưu tập các đối tượng có thể được lấy bằng cách sử dụng vòng lặp for và foreach, trong đó tất cả các phần tử trong một danh sách hoặc bản đồ có thể được lặp lại.
void main() {
List l1 = [];
l1.add(10);
l1.add(20);
l1.add(30);
l1.add(40);
for (var v in l1) {
print(v);
}
}
Trong Đoạn mã trên, đối tượng List l1
được khởi tạo và sau đó, bốn giá trị số nguyên 10, 20, 30 và 40 được thêm vào. Hơn nữa, các phần tử riêng lẻ trong danh sách được in ra bằng cách sử dụng hàm print()
và vòng lặp for.
Hình bên dưới trình bày đầu ra của Đoạn mã.
Xử lý Ngoại lệ và Ngoại lệ
Một ngoại lệ là một điều kiện bất thường làm gián đoạn luồng thực thi bình thường của bất kỳ chương trình nào. Nó có thể xảy ra trong quá trình thực thi (cũng gọi là thời gian chạy) của chương trình đó. Khi một ngoại lệ được ném ra, nó dừng các phần khác của chương trình khỏi hoạt động. Dart có một cách triển khai để xử lý những điều kiện như vậy, sử dụng các trình xử lý ngoại lệ. Trình xử lý ngoại lệ là các câu lệnh được viết để xử lý các ngoại lệ/lỗi và ngăn chương trình dừng đột ngột.
Xử lý Ngoại lệ trong Dart
Mỗi ngoại lệ là một loại con của lớp Exception. Các ngoại lệ xảy ra trong Dart phải được xử lý bằng các trình xử lý ngoại lệ, từ đó ngăn chương trình dừng hoặc kết thúc đột ngột.
a. Khối Try và Catch
Khối try bao gồm mã mà có thể gây ra một ngoại lệ. Khối on được sử dụng để chỉ định ngoại lệ cụ thể cần được xử lý. Khối catch được sử dụng khi một trình xử lý yêu cầu đối tượng Ngoại lệ chung.
void main() {
int a = 5;
int b = 0;
int res;
try {
res = a ~/ b;
} on IntegerDivisionByZeroException {
print('Cannot divide by zero');
}
}
Trong Đoạn mã trên, ba biến a
, b
và res
được khai báo và khởi tạo với các giá trị số nguyên. Mục tiêu là chia a cho b và vì b có giá trị là không, kết quả res
sẽ ném ra một Ngoại lệ.
b. Khối finally
Khối finally bao gồm mã mà nên được thực thi bất kể một Ngoại lệ xảy ra hay không. Nó được đặt sau các khối try/on/catch.
void main() {
int a = 5;
int b = 0;
int res;
try {
res = a ~/ b;
} on IntegerDivisionByZeroException {
print('Cannot divide by zero');
} finally {
print('Finally block executed');
}
}
Trong Đoạn mã trên, khai báo và khởi tạo ba biến a
, b
, và res
với giá trị số nguyên được thực hiện. Mục tiêu là chia a
cho b
và vì giá trị của b
là 0, kết quả res
sẽ ném ra một ngoại lệ. Khối finally
ở đây được thực thi sau khối on
và sẽ in ra câu lệnh bên trong.
Hình bên dưới trình bày đầu ra của Đoạn mã.
c. Ném một Ngoại lệ
Một ngoại lệ có thể được ném một cách rõ ràng bằng cách sử dụng từ khóa throw. Ngoại lệ này phải được xử lý bởi người lập trình để chương trình không kết thúc đột ngột.
void main() {
void checkAge(int age) {
if (age < 0) {
throw FormatException();
}
}
try {
checkAge(-10);
} catch (e) {
print("Age cannot be negative");
}
}
Trong Đoạn mã trên, một hàm checkAge()
được khai báo với một tham số số nguyên là age
. Hàm được kiểm tra tính hợp lệ bằng cách sử dụng điều kiện if
. age được kiểm tra để xem liệu nó có nhỏ hơn không không. Bên trong khối try
, hàm checkAge()
được thực thi và -10 được truyền vào đây cho hàm. Khối catch sẽ kiểm tra xem giá trị -10 có nhỏ hơn không không và nếu có, một FormatException
sẽ được ném bằng cách sử dụng từ khóa throw
.
Hình bên dưới trình bày đầu ra cho Đoạn mã.
Ngoại lệ tích hợp sẵn trong Dart
Dart có các ngoại lệ tích hợp sẵn trong thư viện của nó. Các nhà phát triển có thể xử lý chúng trong mã của họ. Một số ngoại lệ tích hợp sẵn trong Dart như sau:
Loại Ngoại lệ | Mô tả | Ví dụ |
IntegerDivisionByZeroException | Ngoại lệ xảy ra khi chia một số cho số không. | 5 ~/ 0 |
DeferredLoadException | Ngoại lệ xảy ra khi một thư viện bị trì hoãn không thể tải vào Dart. | Khi một thư viện bị trì hoãn như http không được đồng bộ với dự án và không thể tải. |
FormatException | Ngoại lệ xảy ra khi định dạng dữ liệu không đúng. | Khi một chuỗi như John123 được định dạng sai thành một số. |
IOException | Ngoại lệ thường xảy ra khi một tệp văn bản mà người dùng cung cấp dữ liệu không chấp nhận được hoặc không chính xác cho chương trình. | Khi một tệp văn bản như names.txt được đọc, nhưng tệp không được tìm thấy ở vị trí đã chỉ định. |
TimeoutException | Ngoại lệ này được ném khi một hàm không đồng bộ được lập lịch để thực thi trong một khoảng thời gian nhất định mà vượt quá thời gian mong đợi. | Khi một cuộc gọi API được đặt timeout là 100 giây và không có dữ liệu được trả về trong khoảng thời gian đó. |
Ví dụ:
5 ~/ 0
sẽ ném mộtIntegerDivisionByZeroException
.- Khi một thư viện bị trì hoãn như
http
không được đồng bộ với dự án và không thể tải, ngoại lệDeferredLoadException
sẽ được ném. - Khi một chuỗi như
John123
được định dạng sai thành một số, mộtFormatException
sẽ được ném. - Khi một tệp văn bản như
names.txt
được đọc, nhưng tệp không được tìm thấy ở vị trí đã chỉ định, ngoại lệIOException
sẽ được ném. - Khi một cuộc gọi API được đặt timeout là 100 giây và không có dữ liệu được trả về trong khoảng thời gian đó, ngoại lệ
TimeoutException
sẽ được ném.
Lỗi trong Dart
Một lỗi trong Dart được ném ra khi luồng chương trình không được mong đợi. Điều này không nên được bắt, mà được giải quyết bởi người lập trình. Một lỗi chỉ ra rằng mã có thể không chạy được và có thể được sửa chữa bởi người lập trình.
a. Những gì là lỗi trong Dart?
Lỗi xảy ra khi có một vấn đề không được mong đợi. Một số lỗi trong Dart bao gồm lỗi cú pháp, kiểm tra null, hoặc sử dụng các hàm và phương thức không hợp lệ và vân vân.
b. Sự khác biệt giữa Lỗi và Ngoại lệ
Bảng bên dưới dưới đây cho thấy sự khác biệt giữa lỗi và ngoại lệ.
Lỗi | Ngoại lệ |
Xảy ra khi có vấn đề không mong đợi. | Xảy ra khi có điều kiện bất thường xảy ra trong chương trình. |
Thường không nên được bắt và xử lý. | Thường được bắt và xử lý để chương trình không dừng đột ngột. |
Thường chỉ ra một vấn đề nghiêm trọng. | Có thể chỉ ra các tình huống xử lý có thể được thực hiện. |
Điều này cho thấy rằng trong Dart, lỗi thường được xem là các vấn đề nghiêm trọng không thể khắc phục được, trong khi ngoại lệ thường là các tình huống xử lý có thể được kiểm soát và xử lý.