Ответ
В Dart я активно использую Map для различных сценариев. Вот основные подходы:
1. Создание и инициализация Map:
// Литерал Map
final Map<String, int> scores = {
'Alice': 100,
'Bob': 85,
'Charlie': 92,
};
// Конструктор
final emptyMap = Map<String, dynamic>();
final fromConstructor = Map.from(scores); // Копия
// Для неизменяемых данных
const constantMap = const {
'apiUrl': 'https://api.example.com',
'timeout': 30,
};
2. Преобразование коллекций в Map:
// Из List в Map
final users = ['Alice', 'Bob', 'Charlie'];
final userMap = Map.fromIterable(
users,
key: (user) => user.toLowerCase(),
value: (user) => User(name: user),
);
// {'alice': User('Alice'), 'bob': User('Bob'), ...}
// Из двух List (ключи и значения)
final keys = ['id', 'name', 'email'];
final values = [1, 'Alice', 'alice@example.com'];
final map = Map.fromIterables(keys, values);
// {'id': 1, 'name': 'Alice', 'email': 'alice@example.com'}
// Из List<MapEntry>
final entries = [
MapEntry('createdAt', DateTime.now()),
MapEntry('updatedAt', DateTime.now()),
];
final fromEntries = Map.fromEntries(entries);
3. Работа с MapEntry:
// Создание Map через entries
final config = Map.fromEntries([
MapEntry('theme', 'dark'),
MapEntry('notifications', true),
MapEntry('language', 'ru'),
]);
// Итерация по entries
config.entries.forEach((entry) {
print('${entry.key}: ${entry.value}');
});
// Фильтрация через entries
final filtered = Map.fromEntries(
config.entries.where((entry) => entry.value != null),
);
4. Полезные методы Map:
final map = {'a': 1, 'b': 2, 'c': 3};
// Проверка и доступ
if (map.containsKey('a')) {
final value = map['a'];
}
// Безопасное получение с значением по умолчанию
final value = map['d'] ?? 0;
final value2 = map.putIfAbsent('d', () => 0);
// Обновление значений
map.update('a', (v) => v + 1);
map.update('d', (v) => v + 1, ifAbsent: () => 1);
// Удаление
map.remove('b');
map.removeWhere((key, value) => value < 2);
// Преобразование
final doubled = map.map((key, value) => MapEntry(key, value * 2));
5. Кастомные Map для специфичных нужд:
// Case-insensitive Map
class CaseInsensitiveMap<K extends String, V> implements Map<K, V> {
final Map<String, V> _map = {};
@override
V? operator [](Object? key) {
return _map[(key as String).toLowerCase()];
}
@override
void operator []=(K key, V value) {
_map[key.toLowerCase()] = value;
}
// ... остальная реализация Map
}
// Использование
final ciMap = CaseInsensitiveMap<String, int>();
ciMap['Hello'] = 1;
print(ciMap['HELLO']); // 1
6. JSON-сериализация (частая задача в Flutter):
// Map ↔ JSON
final map = {'name': 'Alice', 'age': 30};
final jsonString = jsonEncode(map);
final decodedMap = jsonDecode(jsonString) as Map<String, dynamic>;
// Для сложных объектов с json_serializable
@JsonSerializable()
class User {
final String name;
final int age;
User(this.name, this.age);
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
Map<String, dynamic> toJson() => _$UserToJson(this);
}
7. LinkedHashMap (дефолтная реализация) vs SplayTreeMap vs HashMap:
// Сохраняет порядок вставки (по умолчанию)
final ordered = LinkedHashMap<String, int>();
// Автоматическая сортировка по ключам
final sorted = SplayTreeMap<String, int>();
sorted['z'] = 1;
sorted['a'] = 2;
print(sorted.keys); // (a, z)
// Не гарантирует порядок, но может быть быстрее
final hashMap = HashMap<String, int>();
В Flutter-разработке Map особенно полезны для конфигурации, кэширования данных, передачи параметров между экранами и работы с JSON API. Я часто использую Map.fromIterable для преобразования списков из API в более удобные для работы структуры.