Коллекции в Dart

I. Три встроенные коллекции Dart

В Dart изначально реализованы три стандартные коллекции: List, Set и Map;

Ограничение типа хранимых элементов задаётся черезгенерики (Generics).

Тип коллекцииОсновные свойстваЛитеральный синтаксис
ListУпорядоченный набор, разрешены дубликаты, доступ по цифровому индексу[]
SetБез фиксированного порядка, все элементы уникальны, индексов нет{} (генерик нужно указывать явно, чтобы интерпретатор понял, что это Set)
MapХранит пары «ключ — значение», ключи уникальны, значения могут повторяться{ключ: значение}

II. List Список (упорядоченный массив)

1. Определение и автоматический вывод типов

  • Все массивы в Dart являются объектами типа List, создаются квадратными скобками [];
  • Тип определяется автоматически. Пример: var list = [1,2,3]List<int>; попытка добавить элемент другого типа вызовет ошибку;
  • Поддерживаетсязапятая в конце набора — она не ломает синтаксис и предотвращает ошибки при копировании кода: var list = ['Car', 'Boat', 'Plane',];

2. Основные операции

  1. Правило индексов: нумерация начинается с 0, последний корректный индекс — list.length - 1;
  2. Чтение и изменение элементов: list[index], list[index] = новоеЗначение;
  3. Получение количества элементов: свойство .length;

Пример кода

void main() {
	var list = [1,2,3];
	print(list.length);//Выводит число элементов: 3
	print(list[1]);// Выводит значение 2
	list[1] = 9; //Заменяет второй элемент на 9
	print(list[1]);//Выводит обновлённое значение: 9
}

3. Константный список (неизменяемый во время выполнения)

Перед литералом поставьте ключевое слово const — после инициализации нельзя добавлять, удалять или редактировать элементы:

void main() {
	var constantList = const [1,2,3];
	constantList[1] = 100; // Ошибка компиляции
}
D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Unhandled exception:
Unsupported operation: Cannot modify an unmodifiable list
#0      UnmodifiableListMixin.[]= (dart:_internal/list.dart:89:5)
#1      main (file:///D:/dartdemo/firstdart/bin/firstdart.dart:4:14)
#2      _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:314:19)
#3      _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:193:12)

III. Set Множество

Неупорядоченный набор с уникальными значениями

1. Ключевые особенности Set

  • Отсутствие порядка: у элементов нет фиксированного индекса, доступ через [индекс] невозможен
  • Уникальность элементов: дубликаты удаляются автоматически; повторный ввод одинаковых значений не вызывает ошибку, но в множестве сохранится только одна копия
  • Литеральный маркер: фигурные скобки {}
  • Ограничение по типу через генерики: можно ограничить хранение одним типом; добавление значений другого типа создаст ошибку компиляции или рантайма

Как отличить пустой Set от пустого Map (частая ошибка начинающих)

Пустые скобки {} по умолчанию воспринимаются как Map. Для создания пустого множества обязательно указывайте генерик явно

void main() {
	// Пустой Set строк
	var names = <String>{};
	Set<String> names2 = {}; // Эквивалентная запись

	// Без указания генерика Dart распознаёт как Map<dynamic, dynamic>
	var names3 = {};
}
Автовывод типов
void main() {
	// Автоматически определяется как Set<String>
	var halogens = {'dog', 'cat', 'horse'};

	// Автоматически определяется как Set<int>
	var nums = {1, 2, 6, 2}; // Дубликат числа 2 удаляется, итог {1,2,6}

	// Смешанные типы → вывод Set<Object> (не рекомендуется, теряется контроль типов)
	var mix = {1, "apple"};
}

После вывода типа попытка добавить элемент другого типа создаст ошибку

void main() {
  // В литерале только строки → вывод Set<String>
  var animals = {'dog'};
  print(animals.runtimeType); // _CompactLinkedHashSet<String>

  // Добавление строк того же типа без ошибок
  animals.add('cat');
  animals.add('pig');
  print(animals); // {dog, cat, pig}
  
  // Ошибка: метод add ожидает строку, а получает целое число
  animals.add(1); 
}

2. Добавление, удаление и размер множества

  • Добавить один элемент: .add();
  • Массовое добавление нескольких элементов: .addAll();
  • Получить общее число элементов: .length;
var elements = <String>{};
elements.add('fluorine');
elements.addAll({'chlorine', 'bromine'});
print(elements.length); //3

3. Константное множество Set

Поставьте const перед литералом — добавлять и удалять элементы запрещено:

final constantSet = const {'fluorine', 'chlorine'};
// constantSet.add('helium'); // Вызовет ошибку

IV. Map Словарь (пары ключ-значение)

1. Базовые свойства
  • Хранит информацию в формате пар Ключ — Значение;
  • Ключ уникален для всего словаря, значения могут повторяться сколько угодно;
  • И ключ, и значение могут быть объектами любого типа;
  • Литеральный формат: {ключ: значение};
  • Автоматический вывод типов:
void main() {
	var gifts = {'first': 'partridge'}; // Автовывод Map<String, String> 
	
	var nobleGases = {2: 'helium'}; // Автовывод Map<int, String>
}
2. Два способа создания Map
  • Создание через литерал
var gifts = {'first': 'partridge'};  
  • Создание через конструктор Map (слово new можно опустить)
// Запись через конструктор
var gifts = Map<String, String>();
gifts['first'] = 'partridge';
3. Добавление, чтение и размер словаря
  1. Добавить / перезаписать пару ключ-значение: map[ключ] = значение;
  2. Получить значение по ключу: map[ключ]; если ключа нет — возвращает null;
  3. Получить количество пар ключ-значение: .length;
var gifts = {'first': 'partridge'};
gifts['fourth'] = 'calling birds'; // Добавляем новую пару
print(gifts['first']); // Читаем значение
print(gifts['fifth']);// Обращаемся к несуществующему ключу
print(gifts.length); // Получаем количество пар 
4. Константный словарь Map

Перед литералом поставьте const — пары ключ-значение нельзя изменять:

final constantMap = const {2: 'helium', 10: 'neon'};
// constantMap[2] = 'Helium'; // Вызовет ошибку

V. Расширенные элементы в литералах коллекций

В литералах всех коллекций поддерживаются специальные синтаксические конструкции, разделённые на две группы: базовые элементы и элементы управления потоком для упрощения создания наборов данных.

  1. Базовые элементы: простые выражения, пары ключ-значение, элементы с защитой от null
  2. Элементы управления потоком: оператор spread, условный блок if, цикл for, вложенные комбинации

1. Элементы из простых выражений

Самая простая форма записи: сразу пишите выражение, его результат попадает в коллекцию. В примере второй элемент — результат вычисления 2 * 3, итоговый вывод [1, 6, text]

void main() {
	var list = [1, 2 * 3, 'text'];
	print(list); //[1, 6, text]
}

2. Элементы ключ-значение

Доступны только для литералов Map, формат выражениеКлюч : выражениеЗначение. В примере и ключ, и значение первого элемента вычисляются через математические выражения.

void main() {
	var map = {1+1: 3*8, 'name': 'apple'};
	print(map); //{2: 24, name: apple}
}

3. Элементы с защитой от null (Dart ≥3.8)

Синтаксис ?выражение: элемент добавится в набор только тогда, когда выражение не равно null; при null он полностью игнорируется

Пример с List: переменная a равна null, поэтому она не попадёт в итоговый список без ошибок

int? a = null;
int? b = 3;
var items = [1, ?a, ?b, 5]; // [1, 3, 5]

Пример с Map: символ ? можно ставить как перед ключом, так и перед значением

String? k = null;
int? v = null;
var m = {?k: ?v}; // Если ключ ИЛИ значение null — вся пара удаляется

4. Оператор Spread (развёртка коллекции)

① Обычная развёртка ...

Разворачивает все элементы перебираемой коллекции и вставляет их в текущий набор; разворачивать коллекцию со значением null нельзя

var a = [1,2,3];
var items = [0, ...a, 4]; // [0,1,2,3,4]
② Безопасная развёртка от null ...?

Подходит для коллекций, которые могут быть null: если переменная равна null — она игнорируется без исключений;

Если коллекция не null, но внутри хранит значения null — они сохраняются

List<int>? a = null;
var b = [1, null, 3];
var items = [0, ...?a, ...?b, 4]; // [0, 1, null, 3, 4]

Обязательное правило: все коллекции с допустимым типом null должны использовать оператор ...?, иначе возникнет ошибка компиляции.

5. Условный элемент If

Блок if можно записать прямо внутри коллекции; содержимое добавится только при истинном условии. Поддерживается булева проверка, сопоставление по шаблону if-case и ветка else

Простая булева проверка
void main() {
	bool flag = true;
    var list = [0, if(flag) 1 else 99, 2];
	print(list); //[0, 1, 2]
}
Сопоставление по шаблону if-case (разбор типов и массивов)
void main() {
	Object data = 123;
	var info = [
	  if(data case int i) 'Число: $i',
	  if(data case String s) 'Текст: $s'
	];
	print(info); //[Число: 123]
}

Переменная data имеет тип int, поэтому добавится только строка «Число: 123», ветка для текста игнорируется

6. Элемент цикла For

Цикл for записывается напрямую в коллекцию для генерации множества элементов за один проход. Поддерживаются конструкции for-in и классический трёхчастный цикл

var nums = [2,3,4];

// for-in
var list1 = [1, for(var n in nums) n*n, 7]; // [1,4,9,16,7]

// Трёхчастный цикл for
var list2 = [1, for(var x=5; x>2; x--) x, 7]; // [1,5,4,3,7]

7. Вложенные конструкции управления потоком

if, for и оператор spread свободно вкладываются друг в друга на любом уровне, аналог списковых генераторов из других языков программирования

Пример: оставляем только чётные числа

var numbers = [1,2,3,4,5,6];
var items = [
  0,
  for(var n in numbers)
    if(n.isEven) n,
  8
]; // [0,2,4,6,8]

Пример: вложенный for + if + оператор spread

void main() {
	var ys = [1,2,3];
	var nest = [
	  ...[
		for(var x=0;x<2;x++)
		  for(var y in ys)
			if(x<y) x+y*10
	  ]
	];
	print(nest); //[10, 20, 30, 21, 31]
}

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *