DartのMapについて調べる

はじめに
下記を読んでいく
- 公式ドキュメント > Language tourページ > Mapsセクション
- MapのAPIリファレンス
動作確認にはDartPadを使う

Mapリテラル
MapリテラルはJavaScriptオブジェクトど同様だが、キーに文字列以外も使える
void main() {
final map = {
"one": 1,
"two": 2,
};
print(map); // {one: 1, two: 2}
}

値のアクセス
operator []を使って値にアクセスできる、存在しないキーの場合はnullが返される
void main() {
final map = {
"one": 1,
"two": 2,
};
print(map["one"]); // 1
print(map["two"]); // 2
print(map["three"]); // null
}

値の上書きと追加
operator []=を使って値の上書きができる、存在しないキーの場合は追加される
void main() {
final map = {
"one": 1,
"two": 2,
};
map["one"] = -1;
map["two"] = -2;
map["three"] = -3;
print(map); // {one: -1, two: -2, three: -3}
}

Mapの長さ
lengthプロパティを使ってMapの長さ(key-valueペアの数)を取得できる
void main() {
final map = {
"one": 1,
"two": 2,
};
print(map.length); // 2
}

スプレッド
operator ...を使ってマップを別のマップに展開できる
void main() {
final map1 = {
"one": 1,
"two": 2,
};
final map2 = {
...map1,
"three": 3,
};
print(map2); // {one: 1, two: 2, three: 3}
}

制御フロー
リテラル内でifやforが使える
void main() {
final map = {
if (true) "one": 1,
if (false) "two": 2,
for (var i = 3; i <= 5; i += 1) i.toString(): i,
};
print(map); // {one: 1, 3: 3, 4: 4, 5: 5}
}

定数
Mapリテラルにconstを前置すると定数になる
void main() {
final map1 = {1: 1};
final map2 = {1: 1};
final map3 = const {1: 1};
final map4 = const {1: 1};
print(map1 == map2); // false
print(map3 == map4); // true
}

ここまでLanguage tour
ここからAPIリファレンス

Mapコンストラクタ
空のマップを作成する、<K,V>{}
と同じ
void main() {
final map = Map<String, int>();
print(map); // {}
}

Map.fromコンストラクタ
他のマップからマップを作成する、型は異なっても許容されるがランタイムエラー発生の恐れがある
void main() {
final map1 = <dynamic, dynamic>{
"one": 1,
"two": 2,
};
final map2 = Map<String, int>.from(map1);
print(map2); // {one: 1, two: 2}
}
Map.ofコンストラクタの方が型チェックが静的に行われるので望ましい

Map.fromEntriesコンストラクタ
MapEntryのイテラブルからMapを作成する
void main() {
final entry = MapEntry<String, int>("one", 1);
final map = Map.fromEntries([entry]);
print(entry); // MapEntry(one: 1)
print(map); // {one: 1}
}

Map.fromIterablesコンストラクタ
keyのイテラブルとvalueのイテラブルからMapを作成する
void main() {
final keys = ["one"];
final values = [1];
final map = Map.fromIterables(keys, values);
print(map); // {one: 1}
}

Map.identityコンストラクタ
キーの比較に下記が使用されるらしい
- Object.== → identical
- Object.hashCode → identityHashCode
現時点では謎、試行錯誤のソースコードを備忘のため残しておく
void main() {
final map = Map<List, int>.identity();
final key = const [];
final value = 1;
map[key] = value;
print(identical(key, []));
print(identical(key, key));
print(key == []);
print(key == key);
print(map[key]);
}

Map.ofコンストラクタ
他のマップからマップを作成する、型が同じである必要がある、異なる場合はコンパイルエラー表示
void main() {
final map1 = <dynamic, dynamic>{
"one": 1,
"two": 2,
};
final map2 = Map<String, int>.of(map1);
print(map2); // {one: 1, two: 2}
}
エラーメッセージは下記の通り
The argument type 'Map<dynamic, dynamic>' can't be assigned to the parameter type 'Map<String, int>'.

Map.unmodifiableコンストラクタ
マップから書き込みできないマップを作成する
void main() {
final map1 = Map<String, int>.unmodifiable({
"one": 1,
});
print(map1); // {one: 1}
map1["two"] = 2; // Uncaught Error: Unsupported operation: Cannot modify unmodifiable Map
}

Mapのプロパティ
マップには下記の8つのプロパティがある
- entries: MapEntryのイテラブル
- hashCode: Mapのハッシュコード、中身が同じでも異なる
- isEmpty: 空ならばtrue
- isNotEmpty: 空で無ければtrue
- keys: keyのイテラブル
- length: エントリーの数
- runtimeType: ランタイムの型
- values: valueのイテラブル
void main() {
final map1 = {"one": 1};
final map2 = {"one": 1};
print(map1.entries); // (MapEntry(one: 1))
print(map1.hashCode); // 325431624
print(map2.hashCode); // 264261406
print(map1.isEmpty); // false
print(map1.isNotEmpty); // true
print(map1.keys); // (one)
print(map1.length); // 1
print(map1.runtimeType); // JsLinkedHashMap<String, int>
print(map1.values); // (1)
}

allAllメソッド
同じ型のMapのエントリーを全て追加する
void main() {
final map1 = {"one": 1};
final map2 = {"two": 2, "three": 3};
map1.addAll(map2);
print(map1); // {one: 1, two: 2, three: 3}
}

addEntriesメソッド
同じ型のMapEntryのイテラブルを全て追加する
void main() {
final map = {"one": 1};
final entries = [
MapEntry("two", 2),
MapEntry("three", 3),
];
map.addEntries(entries);
print(map); // {one: 1, two: 2, three: 3}
}

castメソッド
Map<RK, RV>型にキャストする、K is RKかつV is RVである必要がある
void main() {
final map = {"one": 1};
print(map.cast<Object, Object>()); // {one: 1}
print(map.cast<String, String>()); // Uncaught Error: TypeError: 1: type 'JSInt' is not a subtype of type 'String'
}

clearメソッド
Mapを空にする
void main() {
final map = {"one": 1};
map.clear();
print(map); // {}
}

containsKeyメソッド
指定のkeyが含まれるかをチェックする
void main() {
final map = {"one": 1};
print(map.containsKey("one")); // true
}

containsValueメソッド
指定のvalueが含まれるかをチェックする
void main() {
final map = {"one": 1};
print(map.containsValue(1)); // true
}

forEachメソッド
Mapを走査する、keyとvalueの2引数関数を受け取る
void main() {
final map = {"one": 1};
map.forEach((key, value) => {
print("key = $key, value = $value");
}); // key = one, value = 1
}

mapメソッド
Mapを別の型のMapにマッピングする、MapEntryを返す2引数関数を受け取る
void main() {
final map = {"one": 1};
final mapped = map.map((key, value) {
return MapEntry(value, key);
});
print(mapped); // {1: "one"}
}
MapEntryで型推論されるのでmapメソッドへの型引数の指定は不要

putIfAbsentメソッド
Mapに指定のkeyが存在しなければvalueを追加する、valueは関数で指定する
void main() {
final map = {"one": 1};
map.putIfAbsent("one", () => -1);
map.putIfAbsent("two", () => 2);
print(map); // {one: 1, two: 2}
}
戻り値はvalue

removeメソッド
Mapから指定キーを削除して返す、無ければMapには変更を加えずにnullを返す
void main() {
final map = {"one": 1};
map.remove("one");
print(map); // {}
}

removeWhereメソッド
指定の条件を満たすエントリーを削除する
void main() {
final map = {"one": 1, "two": 2, "three": 3};
map.removeWhere((k, v) => v % 2 == 1);
print(map); // {two: 2}
}

toStringメソッド
Mapを文字列表現に変換する
void main() {
final map = {"one": 1, "two": 2, "three": 3};
print(map.toString()); // {one: 1, two: 2, three: 3}
}

updateメソッド
Mapの指定のキーを更新する、第2引数として値の型を受け取って返す関数を受け取る
void main() {
final map = {"count": 0};
map.update("count", (count) => count + 1);
print(map); // {count: 1}
}
第3引数ifAbsentとしてvalueを返す関数を受け取る、キーが存在しない場合に呼び出される
void main() {
final map = {"one": 1};
map.update("count", (count) => count + 1, ifAbsent: () => 0);
print(map); // {one: 1, count: 0}
}
キーが存在しない場合はifAbsentが無ければエラーになる
void main() {
final map = {"one": 1};
map.update("count", (count) => count + 1); // Uncaught Error: Invalid argument (key): Key not in map.: "count"
print(map);
}

updateAllメソッド
Mapの全てのエントリを更新する、valueを返す2引数関数を受け取る
void main() {
final map = {"one": 1, "two": 2};
map.updateAll((k, v) => v * -1);
print(map); // {one: -1, two: -2}
}

オペレーター
Mapには下記3つのオペレーターがある
- operator ==
- operator []
- operator []=
検証についてLanguage tourと重複する部分があるので割愛する

Map.castFrom
マップをキャストする
void main() {
final map = {"one": 1, "two": 2};
final casted = Map.castFrom<String, int, Object, Object>(map);
print(casted); // {one: 1, two: 2}
}
キャスト前後のkeyとvalueの型にはis-a関係がある必要がある

これにて一旦クローズ、次はSetについて調べる