📚

Dartコレクション

List、Set、Map + スプレッド、コレクションfor/if

Dartは List(順序あり重複可)、Set(重複なし集合演算対応)、Map(キー値ペア)の3大コレクションを提供します。スプレッド演算子(...)でコレクション結合、コレクションfor/ifでリテラル内の動的要素生成、map/where/foldメソッドチェーンで強力なデータ変換パイプラインを構成できます。

1. List — 순서 있는 컬렉션

List는 순서가 있고 중복을 허용하는 가장 기본적인 컬렉션입니다. 인덱스로 요소에 접근하며, 다양한 생성 방식을 제공합니다.

List 생성

var fruits = ['사과', '바나나', '오렌지'];\nList<String> names = ['홍길동', '김철수', '이영희'];\nvar emptyList = <int>[];\nvar fixedList = List<int>.filled(5, 0); // [0, 0, 0, 0, 0]\nvar generatedList = List<int>.generate(5, (i) => i * i); // [0, 1, 4, 9, 16]

List 접근 & 조작

var fruits = ['사과', '바나나', '오렌지', '딸기', '포도'];\nprint(fruits[0]); // 사과\nprint(fruits.length); // 5\nprint(fruits.first); // 사과\n\nfruits.add('키위');\nfruits.addAll(['멜론', '수박']);\nfruits.remove('바나나');\nfruits.removeAt(1);\nfruits.removeWhere((fruit) => fruit.length <= 2);\nfruits.sort();\nprint(fruits.indexOf('포도'));\nprint(fruits.contains('사과')); // true

List 변환 (map, where, fold, expand)

var numbers = [1, 2, 3, 4, 5];\nvar doubled = numbers.map((n) => n * 2).toList(); // [2, 4, 6, 8, 10]\nvar evenNumbers = numbers.where((n) => n.isEven).toList(); // [2, 4]\nvar sum = numbers.fold<int>(0, (prev, curr) => prev + curr); // 15\nvar product = numbers.reduce((a, b) => a * b); // 120\n\nvar nested = [[1, 2], [3, 4], [5]];\nvar flattened = nested.expand((list) => list).toList(); // [1, 2, 3, 4, 5]

List 슬라이싱

var numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];\nvar slice = numbers.sublist(2, 5); // [3, 4, 5]\nvar copy = List<int>.from(numbers);\nnumbers.replaceRange(0, 3, [99, 98, 97]);\nvar reversed = numbers.reversed.toList();\nnumbers.fillRange(5, 8, 0);

2. Set — 중복 없는 컬렉션

Set은 중복을 허용하지 않는 컬렉션으로, 수학의 집합 연산(합집합, 교집합, 차집합)을 지원합니다.

Set 생성

var fruits = {'사과', '바나나', '오렌지'};\nSet<String> names = {'홍길동', '김철수', '이영희'};\nvar emptySet = <int>{};\nvar numbers = Set<int>.from([1, 2, 2, 3, 3, 3, 4, 5, 5]); // {1, 2, 3, 4, 5}

Set 집합 연산

var set1 = {1, 2, 3, 4, 5};\nvar set2 = {4, 5, 6, 7, 8};\n\nvar union = set1.union(set2); // {1, 2, 3, 4, 5, 6, 7, 8}\nvar intersection = set1.intersection(set2); // {4, 5}\nvar difference = set1.difference(set2); // {1, 2, 3}\n\nset1.add(6);\nset1.addAll({7, 8, 9});\nset1.remove(9);\nprint(set1.contains(5)); // true\nprint({1, 2}.isSubsetOf(set1)); // true

Set 변환

var numbers = {1, 2, 3, 4, 5};\nvar doubled = numbers.map((n) => n * 2).toSet();\nvar evenNumbers = numbers.where((n) => n.isEven).toSet();\nvar numbersList = numbers.toList();

3. Map — 키-값 쌍 컬렉션

Map은 키-값 쌍으로 데이터를 저장합니다. 키로 빠르게 값을 검색할 수 있습니다.

Map 생성

var person = {\n 'name': '홍길동',\n 'age': 30,\n 'isStudent': false\n};\nMap<String, int> scores = {\n '수학': 90,\n '영어': 85,\n '과학': 95\n};\nvar emptyMap = <String, dynamic>{};\nvar map2 = Map.from({'a': 1, 'b': 2});\nvar map3 = Map.of({'x': 10, 'y': 20});

Map 접근 & 조작

print(person['name']); // 홍길동\nprint(person.containsKey('age')); // true\nprint(person.containsValue(30)); // true\nprint(person.keys.toList());\nprint(person.values.toList());\n\nperson['email'] = 'hong@example.com';\nperson['age'] = 31;\nperson.remove('isStudent');\nperson.addAll({'address': '서울시', 'phone': '010-1234-5678'});\nperson.putIfAbsent('gender', () => '남성');

Map 변환

var scores = {'수학': 90, '영어': 85, '과학': 95, '국어': 80};\n\n// map으로 키-값 변환\nvar scaledScores = scores.map((k, v) => MapEntry(k, v * 1.1));\n\n// entries.where로 필터링\nvar highScores = scores.entries\n .where((entry) => entry.value >= 90);\n\n// forEach로 순회\nscores.forEach((key, value) {\n print('$key: $value');\n});

4. 반복문 (Loops)

Dart는 for, for-in, while, do-while, forEach 등 다양한 반복 방식을 제공합니다.

// 기본 for 루프\nfor (int i = 0; i < 5; i++) {\n print(i);\n}\n\n// for-in 루프\nvar fruits = ['사과', '바나나', '오렌지'];\nfor (var fruit in fruits) {\n print(fruit);\n}\n\n// while 루프\nint count = 0;\nwhile (count < 5) {\n print(count);\n count++;\n}\n\n// forEach 메서드\nvar numbers = [1, 2, 3, 4, 5];\nnumbers.forEach((number) {\n print(number * 2);\n});\n\n// Map forEach\nvar scores = {'수학': 90, '영어': 85, '과학': 95};\nscores.forEach((subject, score) {\n print('$subject: $score점');\n});

루프 제어 (break, continue, 레이블)

// break — 루프 즉시 종료\nfor (int i = 0; i < 10; i++) {\n if (i == 5) break;\n print(i); // 0, 1, 2, 3, 4\n}\n\n// continue — 현재 반복 건너뛰기\nfor (int i = 0; i < 5; i++) {\n if (i == 2) continue;\n print(i); // 0, 1, 3, 4\n}\n\n// 레이블로 중첩 루프 제어\nouterLoop: for (int i = 0; i < 3; i++) {\n for (int j = 0; j < 3; j++) {\n if (i == 1 && j == 1) {\n break outerLoop;\n }\n print('$i, $j');\n }\n}

5. 스프레드 연산자 (...)

스프레드 연산자를 사용하면 컬렉션의 모든 요소를 다른 컬렉션 안에 펼칠 수 있습니다. null-aware 스프레드(...?)도 지원합니다.

var list1 = [1, 2, 3];\nvar list2 = [0, ...list1, 4, 5]; // [0, 1, 2, 3, 4, 5]\n\n// null-aware 스프레드\nList<int>? nullableList;\nvar combined = [0, ...?nullableList, 1]; // [0, 1]\n\n// Map 스프레드\nvar map1 = {'a': 1, 'b': 2};\nvar map2 = {'c': 3, ...map1}; // {'c': 3, 'a': 1, 'b': 2}\n\n// Set 스프레드\nvar set1 = {1, 2, 3};\nvar set2 = {0, ...set1, 4}; // {0, 1, 2, 3, 4}

6. 컬렉션 for / if

컬렉션 리터럴 안에서 forif를 사용하여 동적으로 요소를 생성할 수 있습니다.

// 컬렉션 for\nvar numbers = [1, 2, 3];\nvar doubled = [\n 0,\n for (var n in numbers) n * 2,\n 4\n]; // [0, 2, 4, 6, 4]\n\n// 컬렉션 if\nbool includeZ = true;\nvar letters = ['a', 'b', if (includeZ) 'z']; // ['a', 'b', 'z']\n\n// 실전 활용 — 조건부 UI 리스트 구성\nvar items = [\n 'home',\n if (userLoggedIn) 'profile',\n for (var item in defaultItems) item,\n if (isAdmin) 'admin'\n];

7. 제너레이터 (sync* / async*)

제너레이터 함수는 yield를 사용하여 값을 하나씩 생성합니다. 동기 제너레이터(sync*)는 Iterable을, 비동기 제너레이터(async*)는 Stream을 반환합니다.

동기 제너레이터 (sync*)

Iterable<int> getNumbers(int n) sync* {\n for (int i = 0; i < n; i++) {\n yield i;\n }\n}\n\nvoid main() {\n for (var num in getNumbers(5)) {\n print(num); // 0, 1, 2, 3, 4\n }\n}

비동기 제너레이터 (async*)

Stream<int> countStream(int n) async* {\n for (int i = 1; i <= n; i++) {\n await Future.delayed(Duration(seconds: 1));\n yield i;\n }\n}\n\nvoid main() async {\n await for (var num in countStream(5)) {\n print(num); // 1초마다 1, 2, 3, 4, 5\n }\n}

8. 고급 변환 & 메서드 체이닝

여러 메서드를 체이닝하여 데이터 변환 파이프라인을 구성할 수 있습니다.

var numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];\n\nvar result = numbers\n .where((n) => n % 2 == 0) // 짝수 필터\n .map((n) => n * n) // 제곱\n .takeWhile((n) => n <= 36) // 36 이하만\n .fold(0, (sum, n) => sum + n); // 합계\n\n// 그룹핑\nvar fruits = ['사과', '바나나', '체리', '블루베리', '아보카도'];\nvar byFirstLetter = fruits.fold<Map<String, List<String>>>({}, (map, fruit) {\n var firstLetter = fruit[0];\n map[firstLetter] = (map[firstLetter] ?? [])..add(fruit);\n return map;\n});

9. 캐스케이드 표기법 (..)

캐스케이드 연산자(..)를 사용하면 동일 객체에 연속으로 메서드를 호출할 수 있습니다.

var list = [1, 2, 3]\n ..add(4)\n ..addAll([5, 6])\n ..remove(2)\n ..sort();\n\nvar map = {'user': {'name': '홍길동', 'age': 30}}\n ..['user']['email'] = 'hong@example.com'\n ..['user']['age'] = 31\n ..['active'] = true;

10. Null-Aware 컬렉션 조작

nullable 컬렉션을 안전하게 다루는 패턴입니다.

List<String>? nullableList;\nnullableList?.add('항목'); // null이면 무시\nvar list = nullableList ?? []; // null이면 빈 리스트\nlist.add('항목');\n\nMap<String, int>? scoresMap;\nscoresMap ??= {}; // null이면 빈 맵 할당\nscoresMap['수학'] = 90;

11. 실전 예제 — 학생 데이터 파이프라인

실전에서 자주 사용하는 컬렉션 활용 패턴입니다. 객체 리스트를 map/fold로 변환하여 집계합니다.

class Student {\n final String name;\n final int age;\n final Map<String, int> scores;\n Student(this.name, this.age, this.scores);\n}\n\nvoid main() {\n final students = [\n Student('홍길동', 20, {'수학': 90, '영어': 85, '과학': 95}),\n Student('김철수', 22, {'수학': 75, '영어': 90, '과학': 85}),\n Student('이영희', 21, {'수학': 85, '영어': 92, '과학': 88}),\n Student('박민수', 23, {'수학': 95, '영어': 80, '과학': 92}),\n ];\n\n final averageScores = students.map((student) {\n final total = student.scores.values\n .fold<int>(0, (sum, score) => sum + score);\n final average = total / student.scores.length;\n return {'name': student.name, 'average': average};\n }).toList();\n}

컬렉션 비교 요약

컬렉션순서중복접근 방식주요 용도
ListOO인덱스 [i]순서 있는 데이터, 배열
SetXXcontains()중복 제거, 집합 연산
MapX키 X / 값 O키 [key]키-값 매핑, JSON

핵심 정리

  • List — 가장 범용적. map/where/fold로 함수형 파이프라인 구성
  • Set — 중복 제거가 필요할 때. union/intersection/difference로 집합 연산
  • Map — 키로 빠른 검색. putIfAbsent/entries.where로 조건부 조작
  • 스프레드 & 컬렉션 for/if — 리터럴 안에서 동적 컬렉션 생성
  • 제너레이터sync*(Iterable) / async*(Stream)로 값을 지연 생성

実装ステップ

1

List — add/addAllで追加、map/where/foldで変換・フィルタ・集計、メソッドチェーンでパイプライン構成

2

Set — union(和集合)/intersection(積集合)/difference(差集合)で集合演算、重複除去に有用

3

Map — putIfAbsent(キーない時のみ追加)、entries.whereでフィルタリング、map()でキー値変換

4

スプレッド&コレクションfor/if — [...list1, ...list2]で結合、[for (var x in items) x.name]、[if (flag) item]

メリット

  • メソッドチェーンで関数型スタイルのデータパイプライン構成可能
  • スプレッド/コレクションfor・ifでリテラル内で動的リスト生成

デメリット

  • チェーンが長くなると可読性低下

ユースケース

APIレスポンスJSON配列をモデルリストに変換 データ加工:検索フィルタ、ソート、グルーピング