openplanning

Hướng dẫn và ví dụ Dart Map

  1. Map<K,V>
  2. Constructors
  3. Properties
  4. Methods
  5. addAll(Map<K, V> other)
  6. addEntries(..)
  7. cast<RK, RV>()
  8. castFrom<K, V, K2, V2>(..)
  9. clear()
  10. containsKey(Object? key)
  11. containsValue(Object? value)
  12. forEach(..)
  13. putIfAbsent(..)
  14. remove(Object? key)
  15. removeWhere(..)
  16. update(..)
  17. updateAll(..)

1. Map<K,V>

Trong ngôn ngữ Dart, Lớp Map<K,V> đại diện cho một cấu trúc dữ liệu bao gồm các ánh xạ giữa các khoá (key) và các giá trị (value). Các khoá không được phép trùng nhau, và mỗi khoá sẽ tương ứng với một giá trị.
Một trong các ví dụ tiêu biểu có thể kể tới ở đây là một danh bạ điện thoại, số điện thoại là khoá và tên chủ sở hữu của số điện thoại đó là một giá trị.
map_ex1.dart
void main() {
  Map<String, String> phonebook =  {
    '01000005': 'Tom',
    '01000002': 'Jerry',
    '01000003': 'Tom',
    '01000004': 'Donald'
  };
  Iterable<String> phoneNumbers = phonebook.keys;

  for (var phoneNumber in phoneNumbers) {
    String? name = phonebook[phoneNumber];
    print('Phone Number: $phoneNumber ==> Name: $name');
  }
  print(' ------------- ');
  var phoneNumber = '99999999';
  String? name = phonebook[phoneNumber];
  print('Phone Number: $phoneNumber ==> Name: $name');
}
Output:
Phone Number: 01000005 ==> Name: Tom
Phone Number: 01000002 ==> Name: Jerry
Phone Number: 01000003 ==> Name: Tom
Phone Number: 01000004 ==> Name: Donald
 -------------
Phone Number: 99999999 ==> Name: null
Hệ thống phân cấp các lớp của nhóm Map:
  • LinkedHashMap

2. Constructors

Các constructor:
external factory Map();
factory Map.from(Map other) = LinkedHashMap<K, V>.from;
factory Map.of(Map<K, V> other) = LinkedHashMap<K, V>.of;
external factory Map.unmodifiable(Map<dynamic, dynamic> other);
factory Map.identity() = LinkedHashMap<K, V>.identity;  

factory Map.fromIterable(Iterable iterable,
             {K key(dynamic element)?, V value(dynamic element)?}) = LinkedHashMap<K, V>.fromIterable;  
    
factory Map.fromIterables(Iterable<K> keys, Iterable<V> values) = LinkedHashMap<K, V>.fromIterables;
factory Map.fromEntries(Iterable<MapEntry<K, V>> entries) =>  <K, V>{}..addEntries(entries);
Map()
external factory Map();
Tạo ra một LinkedHashMap rỗng.
Map.from(Map other)
factory Map.from(Map other) = LinkedHashMap<K, V>.from;
Tạo ra một LinkedHashMap với tất cả các ánh xạ được nhập khẩu từ other.
Constructor này không kiểm tra kiểu dữ liệu của other, vì vậy có thể xẩy ra lỗi tại thời gian chạy của ứng dụng. Bạn nên ưu tiên sử dụng constructor Map.of nếu có thể.
Ví dụ:
map_from_ex1.dart
void main()  {
  Map<String, dynamic> other = {
    'A' : 'A1',
    'B' : 'B1',
    'C' : 100  // ---> int type !!!!!!!
  };
  Map<String,String> map = Map.from(other); // Compile OK, but throw Error at runtime!
  print(map); 
}
Output:
Unhandled exception:
type 'int' is not a subtype of type 'String' in type cast
#0      new LinkedHashMap.from.<anonymous closure> (dart:collection/linked_hash_map.dart:88:26)
#1      _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:397:8)
#2      new LinkedHashMap.from (dart:collection/linked_hash_map.dart:87:11)
#3      main
bin/map_from_ex1.dart:7
#4      _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:283:19)
#5      _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)
Map.of(Map<K, V> other)
factory Map.of(Map<K, V> other) = LinkedHashMap<K, V>.of;
Tạo ra một LinkedHashMap với tất cả các ánh xạ được nhập khẩu từ other.
Map.identity()
factory Map.identity() = LinkedHashMap<K, V>.identity;
Trả về một "Identity Map", đây là một Map sử dụng kỹ thuật hashcode và hàm identical để so sánh 2 khoá. (Xem thêm giải thích trong bài viết về LinkedHashMap).
  • Dart LinkedHashMap
Map.unmodifiable(..)
external factory Map.unmodifiable(Map<dynamic, dynamic> other);
Tạo một đối tượng Map không thể sửa đổi (unmodifiable), với tất cả các ánh xạ được nhập khẩu từ other.
Map.fromIterable(..)
factory Map.fromIterable(Iterable iterable,
             {K key(dynamic element)?, V value(dynamic element)?}) = LinkedHashMap<K, V>.fromIterable;
Tạo một đối tượng Map với các khoá và giá trị được tính toán từ các phần tử của một Iterable được chỉ định.
map_fromIterable_ex1.dart
void main()  {
  var iterable = [1, 2, 3, 4, 5];

  var map = Map<int,int>.fromIterable(iterable,
      key : (element)  {
        return element;
      } // Optional Named Parameter.
      ,
      value : (element)  {
         return element * element;
      } // Optional Named Parameter.
  );
  print(map); // {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
}
Map.fromEntries(..)
factory Map.fromEntries(Iterable<MapEntry<K, V>> entries) =>  <K, V>{}..addEntries(entries);
Tạo một Map với các ánh xạ có được từ các MapEntry được cung cấp.
map_fromEntries_ex1.dart
void main() {
  MapEntry<String, String> entry1 = MapEntry('01000005', 'Tom');
  var entry2 = MapEntry('01000002', 'Jerry');
  var entry3 = MapEntry('01000004', 'Donald');

  // Create an Iterable via the List syntax.
  Iterable<MapEntry<String, String>> entries = [entry1, entry2, entry3];
  var phonebook = Map.fromEntries(entries);

  print(phonebook); // {01000005: Tom, 01000002: Jerry, 01000004: Donald}
}

3. Properties

Các property:
Iterable<K> get keys;
Iterable<V> get values;
Iterable<MapEntry<K, V>> get entries;
int get length;
bool get isEmpty;
bool get isNotEmpty;

4. Methods

Các phương thức:
// Operators:
V? operator [](Object? key);

// Methods:
static Map<K2, V2> castFrom<K, V, K2, V2>(Map<K, V> source) => CastMap<K, V, K2, V2>(source);  
    
Map<RK, RV> cast<RK, RV>();
bool containsValue(Object? value);
bool containsKey(Object? key);

void addEntries(Iterable<MapEntry<K, V>> newEntries);
V update(K key, V update(V value), {V ifAbsent()?});
void updateAll(V update(K key, V value));
void removeWhere(bool test(K key, V value));
V putIfAbsent(K key, V ifAbsent());
void addAll(Map<K, V> other);
V? remove(Object? key);
void clear();
void forEach(void action(K key, V value));

5. addAll(Map<K, V> other)

void addAll(Map<K, V> other);
Thêm tất cả các cặp khoá và giá trị của other vào Map này. Nếu khoá đã tồn tại trong Map, giá trị tương ứng của nó sẽ bị thay thế bởi giá trị mới.
map_addAll_ex1.dart
void main() {
  // Map<String,String>
  var options = {
    'configFile': 'config.conf',
    'outDir': './work/out',
    'inputDir': './work/in'
  };
  // Map<String,String>
  var other = {
    'outDir': './data/output',
    'inputDir': './data/input',
    'libraryDir': './libs',
    'logDir': './logs'
  };
  options.addAll(other);
  for (var entry in options.entries) {
    print('${entry.key} --> ${entry.value}');
  }
}
Output:
configFile --> config.conf
outDir --> ./data/output
inputDir --> ./data/input
libraryDir --> ./libs
logDir --> ./logs

6. addEntries(..)

void addEntries(Iterable<MapEntry<K, V>> newEntries);
Thêm tất cả các cặp khoá và giá trị từ các MapEntry vào Map này. Nếu khoá đã tồn tại, giá trị tương ứng của nó sẽ được thay thế bởi giá trị mới.
Ví dụ:
map_addEntries_ex1.dart
void main() {
  // Employee Number --> Salary
  // Map<String,int>
  var empSalaryMap =  {
    'E01': 800,
    'E02': 20000,
    'E03': 700
  };
  MapEntry<String,int> entry3 = MapEntry('E03', 3000);
  var entry4 = MapEntry('E04', 4000);
  var entry5 = MapEntry('E05', 5000);

  // Create an Iterable through the List syntax.
  Iterable<MapEntry<String,int>> newEntries = [entry3, entry4, entry5];
  empSalaryMap.addEntries(newEntries);
  print(empSalaryMap); // {E01: 800, E02: 20000, E03: 3000, E04: 4000, E05: 5000}
}

7. cast<RK, RV>()

Map<RK, RV> cast<RK, RV>();
Trả về một dạng xem của Map<K,V> này dưới dạng Map<RK,RV>.
Ví dụ:
map_cast_ex1.dart
class Person {}
class Employee extends Person {
  String name;
  Employee(this.name);
}
void main() {
  var p1 = Employee('Jennifer');
  var p2 = Employee('James');
  var p3 = Employee('John');
  // int id --> Person
  Map<int, Person> personMap = {11: p1, 21: p2, 23: p3};

  Map<int, Employee> empMap = personMap.cast();
  // or
  var empMap2 = personMap.cast<int, Employee>();

  Iterable<Employee> emps = empMap.values;
  for (var emp in emps) {
    print(emp.name);
  }
}

8. castFrom<K, V, K2, V2>(..)

static Map<K2, V2> castFrom<K, V, K2, V2>(Map<K, V> source) => CastMap<K, V, K2, V2>(source);
Một phương thức tĩnh trả về một dạng xem của một Map<K,V> dưới dạng Map<RK,RV>.
Ví dụ:
map_castFrom_ex1.dart
class Person {}
class Employee extends Person {
  String name;
  Employee(this.name);
}

void main() {
  var p1 = Employee('Jennifer');
  var p2 = Employee('James');
  var p3 = Employee('John');
  // int id --> Person
  Map<int, Person> personMap = {11: p1, 21: p2, 23: p3};

  Map<int, Employee> empMap = Map.castFrom(personMap);
  // or
  var empMap2 = Map.castFrom<int, Person, int, Employee>(personMap);

  Iterable<Employee> emps = empMap2.values;
  for (var emp in emps) {
    print(emp.name);
  }
}

9. clear()

void clear();
Loại bỏ tất cả các ánh xạ có trong Map.
map_clear_ex1.dart
void main() {
  Map<String, String> phonebook = {'01000005': 'Tom', '01000002': 'Jerry'};
  print(phonebook);
  phonebook.clear();
  print(' --- after clear() --- ');
  print(phonebook);
}
Output:
{01000005: Tom, 01000002: Jerry}
 --- after clear() ---
{}

10. containsKey(Object? key)

bool containsKey(Object? key);
Kiểm tra xem một khoá được chỉ định có tồn tại trong Map này hay không.
Ví dụ:
map_containsKey_ex1.dart
void main() {
  var phonebook = <String, String>{'01000005': 'Tom', '01000002': 'Jerry'};
  var key1 = '99999999';
  var contains1 = phonebook.containsKey(key1); // false
  print('contains key ${key1}?  ${contains1}');

  var key2 = '01000005';
  var contains2 = phonebook.containsKey(key2); // true
  print('contains key ${key2}?  ${contains2}');
}
Output:
contains key 99999999?  false
contains key 01000005?  true

11. containsValue(Object? value)

bool containsValue(Object? value);
Kiểm tra xem một giá trị được chỉ định có tồn tại trong Map này hay không.
Ví dụ:
map_containsValue_ex1.dart
void main() {
  Map<String, String> phonebook = {
    '01000005': 'Tom',
    '01000002': 'Jerry',
    '01000003': 'Tom'
  };
  print(phonebook.containsValue('Tom')); // true
  print(phonebook.containsValue('Donald')); // false
}

12. forEach(..)

void forEach(void action(K key, V value));
Gọi hàm action cho mỗi cặp khoá và giá trị của Map này.
Ví dụ:
map_forEach_ex1.dart
void main() {
  // Employee Number --> Salary
  // Map<String,int>
  var empSalaryMap = {
    'E01': 1200,
    'E02': 2000,
    'E03': 1500
  };
  // A Closure
  var action = (String empNumber, int salary) {
    print('Emp Number: $empNumber, salary: $salary');
  };
  empSalaryMap.forEach(action);
}
Output:
Emp Number: E01, salary: 1200
Emp Number: E02, salary: 2000
Emp Number: E03, salary: 1500

13. putIfAbsent(..)

V putIfAbsent(K key, V ifAbsent());
Thêm một ánh xạ vào Map này nếu khoá được chỉ định chưa tồn tại, ngược lại không hành động nào được thực hiện.
map_putIfAbsent_ex1.dart
void main() {
  Map<String, int> scores = {'Bob': 36};
  for (var key in ['Bob', 'Rohan', 'Sophena']) {
    scores.putIfAbsent(key, () => key.length);
  }
  print(scores['Bob']); // 36
  print(scores['Rohan']); //  5
  print(scores['Sophena']); //  7
}

14. remove(Object? key)

V? remove(Object? key);
Loại bỏ ánh xạ tương ứng với khoá được chỉ định.
Ví dụ:
map_remove_ex1.dart
void main() {
  // Employee Number --> Salary
  // Map<String,int>
  var empSalaryMap = {
    'E01': 1200,
    'E02': 2000,
    'E03': 1500
  };
  var salary = empSalaryMap.remove('E02');
  print('Salary: $salary'); // 2000
  print(empSalaryMap); // {E01: 1200, E03: 1500}
}

15. removeWhere(..)

void removeWhere(bool test(K key, V value));
Loại bỏ tất cả các ánh xạ vượt qua kiểm tra bởi hàm được chỉ định.
Ví dụ: Một Map<String,int> chứa các ánh xạ giữa mã số nhân viên và lương. Loại bỏ tất cả các ánh xạ với lương nhỏ hơn 2000.
map_removeWhere_ex1.dart
void main() {
  // Employee Number --> Salary
  // Map<String,int>
  Map<String,int> empSalaryMap = {
    'E01': 1200,
    'E02': 2000,
    'E03': 1500,
    'E04': 1700,
    'E05': 5500
  };
  // A Closure
  var test = (String empNumber, int salary) {
    return salary < 2000;
  };
  empSalaryMap.removeWhere(test);
  print(empSalaryMap); // {E02: 2000, E05: 5500}
}
Output:
{E02: 2000, E05: 5500}

16. update(..)

V update(
    K key,
    V update( V value ),
    {V ifAbsent( )?} // Optional Named Parameter
)
Cập nhập giá trị mới cho khoá được chỉ định.
  • Nếu khoá được chỉ định đã tồn tại trong Map này, giá trị mới sẽ được tính toán bởi hàm update được cung cấp.
  • Nếu khoá được chỉ định không tồn tại trong Map này, hàm ifAbsent sẽ được sử dụng để tính toán giá trị, hoặc giá trị null nếu hàm ifAbsent không được cung cấp.
Ví dụ:
map_update_ex1.dart
void main() {
  // Employee Number --> Salary
  // Map<String,int>
  var empSalaryMap =  {
    'E01': 1500,
    'E02': 2500,
    'E03': 3500
  };
  for(var key in ['E02', 'E05', 'E07']) {
      var newValue = empSalaryMap.update(key,
                                      (value)   {
                                          return value * 2;
                                      },  
                                      ifAbsent: () => 111 // Named Optional Parameter.
                                  );
  }
  print(empSalaryMap); // {E01: 1500, E02: 5000, E03: 3500, E05: 111, E07: 111}
}
Output:
{E01: 1500, E02: 5000, E03: 3500, E05: 111, E07: 111}

17. updateAll(..)

void updateAll(V update(K key, V value));
Cập nhập tất cả các giá trị của Map này, với các giá trị mới được tính toán bởi hàm update được cung cấp.
Ví dụ: Một Map<String,int> chứa các ánh xạ giữa mã số nhân viên và lương. Sử dụng phương thức updateAll để cập nhập lương gấp 2 nếu nó nhỏ hơn 1000.
map_updateAll_ex1.dart
void main() {
  // Employee Number --> Salary
  // Map<String,int>
  var empSalaryMap = {
    'E01': 800,
    'E02': 20000,
    'E03': 700
  };
  // A Closure
  var update = (String empNumber, int salary) {
    if (salary < 1000) {
      return salary * 2;
    }
    return salary;
  };
  empSalaryMap.updateAll(update);
  print(empSalaryMap); // {E01: 1600, E02: 20000, E03: 1400}
}
Output:
{E01: 1600, E02: 20000, E03: 1400}