Dart-Sammlungstypen

Eine Sammlung ist eine Datenstruktur, die zum Speichern mehrerer Datenelemente verwendet wird.

Dart hat drei integrierte Kern-Sammlungstypen: List, Set und Map. In diesem Kapitel wird ihre Verwendung nacheinander vorgestellt.


List: Geordnete Liste

List ist der am häufigsten verwendete Sammlungstyp, der verwendet wird, um eine geordnete Gruppe von Elementen zu speichern, wobei Duplikate erlaubt sind.

Sie können auf jedes Element in einer List über seinen Index zugreifen (beginnend bei 0).

List erstellen

Beispiel

void main() {
  // Eine List mit Literalen erstellen
  List<String> sites = ['EXAMPLE', 'Google', 'GitHub'];
  print(Website-Liste: $sites);

  // Typinferenz (generische Typen ausgelassen)
  var numbers = [1, 2, 3, 4, 5];
  print(Zahlenliste: $numbers);

  // Eine leere Liste erstellen
  var emptyList = <String>[];

  // Eine Liste fester Länge mit List.filled erstellen
  var zeros = List.filled(3, 0);  // [0, 0, 0]
  print(Gefüllte Liste: $zeros);

  // Eine Liste mit List.generate erstellen
  var squares = List.generate(5, (i) => i * i);
  print(Liste der Quadratzahlen: $squares);
}
网站列表: [EXAMPLE, Google, GitHub]
数字列表: [1, 2, 3, 4, 5]
填充列表: [0, 0, 0]
平方数列表: [0, 1, 4, 9, 16]

Zugriff auf und Ändern von Listenelementen

Beispiel

void main() {
  var fruits = [Apfel, Banane, Orange];

  // Zugriff über Index (beginnend bei 0)
  print('Erste Frucht: ${fruits[0]}');  // Apfel
  print('Letzte: ${fruits[fruits.length - 1]}');  // Orange

  // Element ändern
  fruits[1] = 'Traube';
  print('Nach der Änderung: $fruits');  // [Apfel, Traube, Orange]

  // Länge abrufen
  print('Anzahl der Früchte: ${fruits.length}');

  // Prüfen, ob leer
  print('Ist die Liste leer? ${fruits.isEmpty}');
  print('Ist die Liste nicht leer? ${fruits.isNotEmpty}');
}
第一个水果: 苹果
最后一个: 橙子
修改后: [苹果, 葡萄, 橙子]
水果数量: 3
列表为空? false
列表非空? true

Gängige Listenoperationen

Beispiel

void main() {
  var list = ['EXAMPLE', 'Dart'];

  // Elemente hinzufügen
  list.add('Flutter');           // Einzelnes Element hinzufügen
  list.addAll(['Google', 'AI']);  // Mehrere Elemente hinzufügen
  print('Nach dem Hinzufügen: $list');

  // Element an bestimmter Position einfügen
  list.insert(1, 'Tutorial');
  print('Nach dem Einfügen: $list');

  // Elemente löschen
  list.remove('AI');             // Nach Wert löschen
  list.removeAt(0);              // Nach Index löschen
  list.removeLast();             // Letztes löschen
  print('Nach dem Löschen: $list');

  // Element finden
  bool hasDart = list.contains('Dart');
  int index = list.indexOf('Dart');
  print('Enthält Dart? $hasDart, Position: $index');

  // Sortieren
  var nums = [3, 1, 4, 1, 5, 9];
  nums.sort();
  print('Nach dem Sortieren: $nums');

  // Umkehren
  var reversed = nums.reversed.toList();
  print('Nach Umkehrung: $reversed');

  // Unterliste abrufen
  var subList = nums.sublist(0, 3);
  print('Erste 3: $subList');
}
添加后: [EXAMPLE, Dart, Flutter, Google, AI]
插入后: [EXAMPLE, 教程, Dart, Flutter, Google, AI]
After deletion: [教程, Dart, Flutter]
包含 Dart? true, 位置: 1
排序后: [1, 1, 3, 4, 5, 9]
反转后: [9, 5, 4, 3, 1, 1]
前3个: [1, 1, 3]

Funktionale Methoden auf List

Darts List unterstützt funktionale Methoden wie map, where und reduce, wodurch die Datenverarbeitung prägnanter wird.

Beispiel

void main() {
  var scores = [55, 78, 92, 60, 45, 88];

  // where: Filter (Elemente beibehalten, die die Bedingung erfüllen)
  var passed = scores.where((s) => s >= 60);
  print('Bestandene Punktzahl: $passed');

  // map: Abbildung (jedes Element in einen neuen Wert umwandeln)
  var grades = scores.map((s) => s >= 60 ? 'Bestanden' : 'Nicht bestanden');
  print('Note: $grades');

  // Verketteter Aufruf von where + map
  var highScores = scores
      .wobei((s) => s >= 80)
      .map((s) => 'Highscore: $s')
      .toList();
  print('Highscore-Liste: $highScores');

  // reduce: kumulative Berechnung
  var total = scores.reduce((sum, s) => sum + s);
  print('BEISPIEL Gesamtpunktzahl: $total');

  // fold: kumulative Berechnung mit einem Anfangswert
  var avg = scores.fold(0, (sum, s) => sum + s) / scores.length;
  print('Durchschnittliche Punktzahl: ${avg.toStringAsFixed(1)}');

  // any / every: Existenzprüfung
  bool hasFullMark = scores.any((s) => s == 100);
  bool allPassed = scores.every((s) => s >= 60);
  print('Gibt es eine perfekte Punktzahl? $hasFullMark');
  print('Alle bestanden? $allPassed');
}
及格分数: (78, 92, 60, 88)
评级: (不及格, 及格, 及格, 及格, 不及格, 及格)
高分列表: [高分: 92, 高分: 88]
EXAMPLE 总分: 418
平均分: 69.7
有满分吗? false
全部及格? false

map() und where() geben ein Iterable zurück (verzögerte Auswertung). Du musst es mit toList() in eine List umwandeln, damit die Berechnung tatsächlich ausgeführt wird. Wenn du nur einmal iterieren musst, kannst du das Iterable direkt verwenden.


Set: Sammlung eindeutiger Elemente

Set ist ähnlich wie List, aber jedes Element in einem Set kann nur einmal vorkommen; Duplikate sind nicht erlaubt.

Set ist ungeordnet, und auf Elemente kann nicht per Index zugegriffen werden.

Beispiel

void main() {
  // Ein Set erstellen (Elemente werden automatisch dedupliziert)
  Set<String> tags = {'Dart', 'Flutter', 'Dart', 'EXAMPLE'};
  print('Tag-Menge: $tags');  // {Dart, Flutter, EXAMPLE}, das doppelte Dart wurde entfernt
  print('Tag-Anzahl: ${tags.length}');  // 3

  // Elemente hinzufügen
  tags.add('Google');
  tags.add('Dart');  // Dart ist bereits vorhanden, es wird nicht erneut hinzugefügt
  print('Nach dem Hinzufügen: $tags');

  // Elemente löschen
  tags.remove('Google');
  print('Nach dem Löschen: $tags');

  // Prüfen, ob enthalten
  print('Enthält EXAMPLE? ${tags.contains('EXAMPLE')}');

  // Mengenoperationen
  var setA = {1, 2, 3, 4};
  var setB = {3, 4, 5, 6};

  print('Schnittmenge: ${setA.intersection(setB)}');  // {3, 4}
  print('Vereinigung: ${setA.union(setB)}');          // {1, 2, 3, 4, 5, 6}
  print('Differenz: ${setA.difference(setB)}');     // {1, 2}
}
标签集合: {Dart, Flutter, EXAMPLE}
标签数量: 3
添加后: {Dart, Flutter, EXAMPLE, Google}
After deletion: {Dart, Flutter, EXAMPLE}
包含 EXAMPLE? true
交集: {3, 4}
并集: {1, 2, 3, 4, 5, 6}
差集: {1, 2}

Der typische Anwendungsfall für Set ist die Deduplizierung. Wenn keine doppelten Elemente benötigt werden, ist die Verwendung von Set viel effizienter als das manuelle Deduplizieren mit List.


Map: Zuordnung von Schlüssel-Wert-Paaren

Map wird verwendet, um Schlüssel-Wert-Paare zu speichern. Jeder Schlüssel entspricht einem Wert.

Schlüssel in einer Map müssen eindeutig sein; Werte können dupliziert werden.

Erstellen und Zugreifen auf Map

Beispiel

void main() {
  // Map mit Literalen erstellen
  Map<String, String> siteInfo = {
    'name': 'EXAMPLE',
    'url': 'https://www.example.com',
    'type': 'Programmier-Tutorial',
  };

  // Wert über Schlüssel abrufen
  print('Seitenname: ${siteInfo['name']}');
  print('Seiten-URL: ${siteInfo['url']}');

  // Zugriff auf einen nicht vorhandenen Schlüssel gibt null zurück
  print('Beschreibung: ${siteInfo['description']}');  // null

  // Schlüssel-Wert-Paare hinzufügen/ändern
  siteInfo['language'] = 'Chinesisch';
  siteInfo['name'] = 'example.com';  // Wert eines vorhandenen Schlüssels ändern
  print('Nach Aktualisierung: $siteInfo');

  // Alle Schlüssel und alle Werte abrufen
  print('Alle Schlüssel: ${siteInfo.keys}');
  print('Alle Werte: ${siteInfo.values}');

  // Prüfen, ob Schlüssel existiert
  print('Enthält url-Schlüssel? ${siteInfo.containsKey('url')}');
  print('Enthält desc-Schlüssel? ${siteInfo.containsKey('desc')}');
}
站点名称: EXAMPLE
站点 URL: https://www.example.com
描述: null
更新后: {name: example.com, url: https://www.example.com, type: 编程教程, language: 中文}
所有键: (name, url, type, language)
所有值: (example.com, https://www.example.com, 编程教程, 中文)
有 url 键? true
有 desc 键? false

Gängige Map-Operationen

Beispiel

void main() {
  var scores = {
    'example': 95,
    'Alice': 87,
    'Bob': 72,
  };

  // Über Map iterieren
  scores.forEach((name, score) {
    print('$name: $score Punkte');
  });

  // Schlüssel-Wert-Paar löschen
  scores.remove('Bob');
  print('Nach dem Löschen von Bob: $scores');

  // Wert oder Standardwert abrufen
  int aliceScore = scores['Alice'] ?? 0;
  int eveScore = scores['Eve'] ?? 0;  // Falls nicht vorhanden, Standardwert 0 zurückgeben
  print('Alice: $aliceScore, Eve: $eveScore');

  // putIfAbsent: nur hinzufügen, wenn der Schlüssel nicht vorhanden ist
  scores.putIfAbsent('example', () => 100);  // Existiert bereits, nicht hinzufügen
  scores.putIfAbsent('David', () => 80);    // Falls nicht vorhanden, hinzufügen
  print('Endstand: $scores');

  // Länge abrufen
  print('Anzahl der Personen: ${scores.length}');
}
example: 95 分
Alice: 87 分
Bob: 72 分
删除 Bob 后: {example: 95, Alice: 87}
Alice: 87, Eve: 0
最终: {example: 95, Alice: 87, David: 80}
人数: 3

Schlüssel in einer Map können von jedem Typ sein (String, int usw.), aber die Gleichheit von Schlüsseln muss aussagekräftig sein. Wenn benutzerdefinierte Objekte als Schlüssel verwendet werden, müssen Sie sicherstellen, dass == und hashCode korrekt implementiert sind.


Collection-Spread-Operatoren ... und ...?

Der Spread-Operator kann die Elemente einer Collection in eine andere Collection verteilen.

Beispiel

void main() {
  var basics = ['Dart', 'Flutter'];
  var advanced = ['Asynchrone Programmierung', 'Zustandsverwaltung'];

  // ... Spread-Operator: Elemente aus einer anderen Collection einfügen
  var allCourses = ['BEISPIEL Erste Schritte', ...basics, ...advanced];
  print('Alle Kurse: $allCourses');

  // ...? Null-aware-Spread: überspringen, wenn die Collection null ist
  List<String>? optionalList;  // Kann null sein
  var safeList = ['Erstes Element', ...?optionalList];
  print('Sicherer Spread: $safeList');  // Nur das erste Element

  optionalList = ['Zusätzlicher Inhalt'];
  safeList = ['Erstes Element', ...?optionalList];
  print('Non-Null-Spread: $safeList');
}
所有课程: [EXAMPLE 入门, Dart, Flutter, 异步编程, 状态管理]
安全展开: [第一项]
有值展开: [第一项, 额外内容]

if und for in Sammlungen (Kontrollfluss in Sammlungen)

Dart ermöglicht die direkte Verwendung von if und for in Sammlungsliteralen; das ist sehr nützlicher syntaktischer Zucker.

Beispiel

void main() {
  bool showAdmin = true;

  // Sammlungs-if: Elemente bedingt einschließen
  var menuItems = [
    'Startseite',
    'Tutorial',
    if (showAdmin) 'Admin-Panel',  // Nur hinzufügen, wenn die Bedingung wahr ist
    'Über',
  ];
  print('Menü: $menuItems');

  // for in einer Sammlung: Elemente aus einer anderen Sammlung erzeugen
  var numbers = [1, 2, 3];
  var doubled = [
    for (var n in numbers) n * 2,  // Iterieren, um neue Elemente zu erzeugen
  ];
  print(Verdoppelt: $doubled);

  // if + for können kombiniert werden
  var tags = ['Dart', 'Flutter'];
  var links = [
    'BEISPIEL Startseite',
    for (var tag in tags) 'https://example.com/$tag',
  ];
  print('Links: $links');
}
菜单: [首页, 教程, 管理后台, 关于]
翻倍: [2, 4, 6]
链接: [EXAMPLE 首页, https://example.com/Dart, https://example.com/Flutter]

Vergleich von drei Sammlungen

FunktionenListeSetMap
ReihenfolgeGeordnetUngeordnetUngeordnet (hat aber eine Iterationsreihenfolge)
Duplikate erlaubtErlaubtNicht erlaubtSchlüssel müssen eindeutig sein, Werte dürfen mehrfach vorkommen
IndexzugriffUnterstützt []Nicht unterstütztZugriff über Schlüssel []
Typische SzenarienGeordnete Liste, SequenzDeduplizierung, Tag-SammlungSchlüssel-Wert-Zuordnung, Konfigurationselemente
Literale erstellen[]{}{key: value}

Hinweise

Sowohl Set als auch Map verwenden{}als Literal.

Der Unterschied ist:

  • Map-Verwendungkey: valueForm (mit Doppelpunkt)
  • Set schreibt Elemente direkt (ohne Doppelpunkt)

Zum Beispiel:

var map = {'name': 'Tom'};   // Map
var set = {'Tom', 'Jerry'}; // Set

Besonders zu beachten:

var x = {};

Dies erzeugt kein leeres Set; stattdessen leitet Dart es ab als:

Map<dynamic, dynamic>

Da leere geschweifte Klammern den Elementtyp nicht bestimmen können, behandelt Dart sie standardmäßig als Map.

Um ein leeres Set zu erstellen, müssen Sie explizit angeben:

var x = <String>{};

oder

:
Set<String> x = {};
Weitere Erweiterungen