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('사과')); // trueList 변환 (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)); // trueSet 변환
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
컬렉션 리터럴 안에서 for와 if를 사용하여 동적으로 요소를 생성할 수 있습니다.
// 컬렉션 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}컬렉션 비교 요약
| 컬렉션 | 순서 | 중복 | 접근 방식 | 주요 용도 |
|---|---|---|---|---|
| List | O | O | 인덱스 [i] | 순서 있는 데이터, 배열 |
| Set | X | X | contains() | 중복 제거, 집합 연산 |
| Map | X | 키 X / 값 O | 키 [key] | 키-값 매핑, JSON |
핵심 정리
- List — 가장 범용적.
map/where/fold로 함수형 파이프라인 구성 - Set — 중복 제거가 필요할 때.
union/intersection/difference로 집합 연산 - Map — 키로 빠른 검색.
putIfAbsent/entries.where로 조건부 조작 - 스프레드 & 컬렉션 for/if — 리터럴 안에서 동적 컬렉션 생성
- 제너레이터 —
sync*(Iterable) /async*(Stream)로 값을 지연 생성
実装ステップ
List — add/addAllで追加、map/where/foldで変換・フィルタ・集計、メソッドチェーンでパイプライン構成
Set — union(和集合)/intersection(積集合)/difference(差集合)で集合演算、重複除去に有用
Map — putIfAbsent(キーない時のみ追加)、entries.whereでフィルタリング、map()でキー値変換
スプレッド&コレクションfor/if — [...list1, ...list2]で結合、[for (var x in items) x.name]、[if (flag) item]
メリット
- ✓ メソッドチェーンで関数型スタイルのデータパイプライン構成可能
- ✓ スプレッド/コレクションfor・ifでリテラル内で動的リスト生成
デメリット
- ✗ チェーンが長くなると可読性低下