📜

【Dart】List,Set,Mapの作り方

25 min read

簡単な定義方法・特徴を実行結果とともに見ていきます。

関数については、【Dart】List,Set,Mapの便利な関数を参照してください。

【Dart】List,Set,Map(コレクション)の作り方

はじめに

事前知識

  • List,Set,MapはGenerics(総称型):List<E>Set<E>Map<K, V>

  • Generics(総称型)のため、定義した際に指定した型に合った入れ物が用意される

    • 例えば、var list = List<String>();のように定義すると、list.dartのfactory行external factory List([int length]);が実行される
      • List<E>はabstractクラスだが、実際にはfactory コンストラクタが呼ばれるのでオブジェクト化可能

list.dart

abstract class List<E> implements EfficientLengthIterable<E> {
  /**
   * Creates a list of the given length.
   *
   * The created list is fixed-length if [length] is provided.
   *
   *     List fixedLengthList = new List(3);
   *     fixedLengthList.length;     // 3
   *     fixedLengthList.length = 1; // Error
   *
   * The list has length 0 and is growable if [length] is omitted.
   *
   *     List growableList = new List();
   *     growableList.length; // 0;
   *     growableList.length = 3;
   *
   * To create a growable list with a given length, just assign the length
   * right after creation:
   *
   *     List growableList = new List()..length = 500;
   *
   * The [length] must not be negative or null, if it is provided.
   */
  external factory List([int length]);

  /**
   * Creates a list of the given length with [fill] at each position.
・
・
・
【省略】

iterable.dart

abstract class EfficientLengthIterable<T> extends Iterable<T> {
  const EfficientLengthIterable();
  /**
   * Returns the number of elements in the iterable.
   *
   * This is an efficient operation that doesn't require iterating through
   * the elements.
   */
  int get length;
}

List,Set,Mapの基本例

  • 推奨された空の定義方法
void main() {
  var testList = <String>[];
  var testSet = <String>{};
  var testMap = <String, String>{};
}

以下良くない例

void main() {
  var testList = List();
  var testSet = Set();
  var testMap = Map();
}
  • 基本的な例
    • List:値の重複可
    • Set:値の重複不可
    • Map:左側のkey値は重複不可
void main() {
  var testList = <String>['Apple', 'Apple', 'Peach'];
  var testSet = <String>{'Apple', 'Strawberry', 'Peach'};
  var testMap = <String, String>{
    'A': 'Apple',
    'B': 'Apple',
    'C': 'Peach',
  };
  print(testList);
  print(testSet);
  print(testMap);
}

実行結果

[Apple, Apple, Peach]
{Apple, Strawberry, Peach}
{A: Apple, B: Apple, C: Peach}

List

定義(格納)

  • 他のListの変数を追加
void main() {
  var list1 = ['Apple', 'Strawberry', 'Peach'];
  var list2 = ['Cherry', 'Litchi', ...list1];
  print(list2);
}

実行結果

[Cherry, Litchi, Apple, Strawberry, Peach]
  • 空の場合は無視される
void main() {
  var list1 = <String>[];
  var list2 = ['Cherry', 'Litchi', ...list1];
  print(list2);
}

実行結果

[Cherry, Litchi]
  • nullの場合は実行時エラーとなる
void main() {
  var list1 = <String>[];
  list1 = null;
  var list2 = ['Cherry', 'Litchi', ...list1];
  print(list2);
}

実行結果

[ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: NoSuchMethodError: The getter 'iterator' was called on null.
Receiver: null
Tried calling: iterator
#0      Object.noSuchMethod (dart:core-patch/object_patch.dart:53:5)
#1      main (package:flutter_snake/main.dart:4:39)
#2      _runMainZoned.<anonymous closure>.<anonymous closure> (dart:ui/hooks.dart:241:25)
#3      _rootRun (dart:async/zone.dart:1184:13)
#4      _CustomZone.run (dart:async/zone.dart:1077:19)
#5      _runZoned (dart:async/zone.dart:1619:10)
#6      runZonedGuarded (dart:async/zone.dart:1608:12)
#7      _runMainZoned.<anonymous closure> (dart:ui/hooks.dart:233:5)
#8      _startIsolate.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:301:19)
#9      _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:168:12)

※Android Studio実行

  • null対策の?を付けることで、無視される
void main() {
  var list1 = <String>[];
  list1 = null;
  var list2 = ['Cherry', 'Litchi', ...?list1];
  print(list2);
}

実行結果

[Cherry, Litchi]
  • if文による追加:true版
    • 注意
      • constのListを作成する場合は、if文の条件にconst値を利用する必要がある
        • 例えば、final bool isTest = true;とし、if(isTest) 'Banana',とした場合は以下コンパイルエラー(const bool isTest = true;は可)
          • The values in a const list literal must be constants
void main() {
  final list = [
    'Cherry',
    'Litchi',
    if (true) 'Banana',
  ];
  print(list);
}

実行結果

[Cherry, Litchi, Banana]
  • if文による追加:false版
void main() {
  final list = [
    'Cherry',
    'Litchi',
    if (false) 'Banana',
  ];
  print(list);
}

実行結果

[Cherry, Litchi]
  • for文を利用した追加
    • 以下の場合は、listはList<Object>となるためStringもintも格納可能
    • ※constのListは作成不可
void main() {
  final list = [
    'Cherry',
    'Litchi',
    for (int i = 0; i < 3; i++) i,
  ];
  print(list);
}

実行結果

[Cherry, Litchi, 0, 1, 2]
  • for文を利用した追加
    • 簡単に同じ中身を指定数格納可能
void main() {
  final list = [
    'Cherry',
    'Litchi',
    for (int i = 0; i < 3; i++)'Banana',
  ];
  print(list);
}

実行結果

[Cherry, Litchi, Banana, Banana, Banana]

簡単な個別確認

  • 添え字
void main() {
  var list = ['Apple', 'Strawberry', 'Peach'];
  print(list[0]);
  print(list[1]);
  print(list[2]);
}

実行結果

Apple
Strawberry
Peach
  • for文で取得
void main() {
  var list = ['Apple', 'Strawberry', 'Peach'];
  for (var value in list) {
    print(value);
  }
}

実行結果

Apple
Strawberry
Peach
  • forEach文で取得
void main() {
  var list = ['Apple', 'Strawberry', 'Peach'];
  list.forEach(print);
}

実行結果

Apple
Strawberry
Peach

簡単な変更方法

  • 添え字
    • ※存在しない値list[3] = 'Banana';では実行時エラーとなる
void main() {
  var list = ['Apple', 'Strawberry', 'Peach'];
  list[0] = 'Banana';
  print(list);
}

実行結果

[Banana, Strawberry, Peach]

Growable(可変長と固定長)

  • lengthの変更(Growable = 可変長の確認)
    • 空の場合は、長さは0
    • 長さ0から増やすと、null値となる
void main() {
  final list = [];
  print(list.length);
  list.length = 3;
  print(list.length);
  print(list);
}

実行結果

0
3
[null, null, null]
  • lengthの変更(Not Growable = 固定長の確認)
    • List(10)により長さを10と固定すると、その後に変更すると実行時エラー
void main() {
  final list = List(10);
  print(list.length);
  list.length = 3;
  print(list.length);
  print(list);
}

実行結果

10
[ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: Unsupported operation: Cannot change the length of a fixed-length list
#0      FixedLengthListMixin.length= (dart:_internal/list.dart:17:5)
#1      main (package:flutter_snake/main.dart:4:8)
#2      _runMainZoned.<anonymous closure>.<anonymous closure> (dart:ui/hooks.dart:241:25)
#3      _rootRun (dart:async/zone.dart:1184:13)
#4      _CustomZone.run (dart:async/zone.dart:1077:19)
#5      _runZoned (dart:async/zone.dart:1619:10)
#6      runZonedGuarded (dart:async/zone.dart:1608:12)
#7      _runMainZoned.<anonymous closure> (dart:ui/hooks.dart:233:5)
#8      _startIsolate.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:301:19)
#9      _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:168:12)

※Android Studio実行

便利なfactoryコンストラクタ

  • List<E>.filled
    • 指定長さ分、指定値で埋めたListを作成
    • growableはデフォルトfalse
void main() {
  final list = List<String>.filled(3, 'Banana', growable: true);
  print(list);
}

実行結果

[Banana, Banana, Banana]
  • List<E>.unmodifiable
    • List([]形式)を引数にとり、中身も長さも不変のListを作成
      • その後に変更は不可
        • list2.add('TEST');list2.length = 1;とすると、実行時エラー
        • list2 = xxxとすると、以下コンパイルエラー
          • The final variable 'list2' can only be set once
void main() {
  var list1 = ['Apple', 'Strawberry', 'Peach'];
  final list2 = List<String>.unmodifiable(list1);
  print(list2);
}

実行結果

[Apple, Strawberry, Peach]
  • List<E>.generate
    • 指定長さ分、generatorを利用し、好きな値を格納したListを作成
    • growableはデフォルトtrue
void main() {
  final list = List<int>.generate(3, (int index) => index * index);
  print(list);
}

実行結果

[0, 1, 4]
  • List<E>.generate
    • 添え字に利用する例
void main() {
  var list1 = ['Apple', 'Strawberry', 'Peach'];
  final list2 = List<String>.generate(3, (int index) => list1[index]);
  print(list2);
}

実行結果

[Apple, Strawberry, Peach]

Set

定義(格納)

  • Setを定義したい場合、{}はMapに注意
void main() {
  var testSetBad = {};
  var testSetGood = <String>{};
  
  print(testSetBad.runtimeType);
  print(testSetGood.runtimeType);
}

実行結果

JsLinkedHashMap<dynamic, dynamic>
_LinkedHashSet<String>

※☝DartPad実行

_InternalLinkedHashMap<dynamic, dynamic>
_CompactLinkedHashSet<String>

※☝Android Studio実行

上記の違いは、下のようにSetもSet<E>LinkedHashSet<E>external factory LinkedHashSetと呼ばれるように、externalが付く外部コードを実行しているためと推測
(Dart VM、SDKとexternal先が異なる)

set.dart

abstract class Set<E> extends EfficientLengthIterable<E> {
・
・
・
  factory Set() = LinkedHashSet<E>;

linked_hash_set.dart

abstract class LinkedHashSet<E> implements Set<E> {
・
・
・
  external factory LinkedHashSet(
      {bool equals(E e1, E e2),
      int hashCode(E e),
      bool isValidKey(potentialKey)});
  • 重複した値を格納しようとすると無視され、一つ目のみ格納される
    • <String>は無くても可
void main() {
  var set = <String>{'Apple', 'Apple', 'Strawberry', 'Peach', 'Apple'};
  print(set);
}

実行結果

{Apple, Strawberry, Peach}
  • 他のSetの変数を追加
    • なお、重複した値を格納しようとすると無視され、一つ目のみ格納される
void main() {
  var set1 = {'Apple', 'Strawberry', 'Peach'};
  var set2 = {'Apple', 'Cherry', 'Litchi', ...set1};
  print(set2);
}

実行結果

{Apple, Cherry, Litchi, Strawberry, Peach}
  • 空の場合は無視される
void main() {
  var set1 = <String>{};
  var set2 = {'Apple', 'Cherry', 'Litchi', ...set1};
  print(set2);
}

実行結果

{Apple, Cherry, Litchi}
  • nullの場合は実行時エラーとなる
void main() {
  var set1 = <String>{};
  set1 = null;
  var set2 = {'Apple', 'Cherry', 'Litchi', ...set1};
  print(set2);
}

実行結果

[ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: NoSuchMethodError: The getter 'iterator' was called on null.
Receiver: null
Tried calling: iterator
#0      Object.noSuchMethod (dart:core-patch/object_patch.dart:53:5)
#1      main (package:flutter_snake/main.dart:4:47)
#2      _runMainZoned.<anonymous closure>.<anonymous closure> (dart:ui/hooks.dart:241:25)
#3      _rootRun (dart:async/zone.dart:1184:13)
#4      _CustomZone.run (dart:async/zone.dart:1077:19)
#5      _runZoned (dart:async/zone.dart:1619:10)
#6      runZonedGuarded (dart:async/zone.dart:1608:12)
#7      _runMainZoned.<anonymous closure> (dart:ui/hooks.dart:233:5)
#8      _startIsolate.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:301:19)
#9      _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:168:12)

※Android Studio実行

  • null対策の?を付けることで、無視される
void main() {
  var set1 = <String>{};
  set1 = null;
  var set2 = {'Apple', 'Cherry', 'Litchi', ...?set1};
  print(set2);
}

実行結果

{Apple, Cherry, Litchi}
  • 他のlistの変数も同様に追加可能
    • なお、重複した値を格納しようとすると無視され、一つ目のみ格納される
void main() {
  var list = ['Apple', 'Strawberry', 'Peach'];
  var set = {'Apple', 'Cherry', 'Litchi', ...list};
  print(set);
}

実行結果

{Apple, Cherry, Litchi, Strawberry, Peach}
  • 上記と同様のことをaddAll関数で可能
void main() {
  final set = {'Apple', 'Cherry', 'Litchi'};
  set.addAll(['Apple', 'Strawberry', 'Peach']);
  print(set);
}

実行結果

{Apple, Cherry, Litchi, Strawberry, Peach}
  • if文による追加:true版
    • 注意
      • constのSetを作成する場合は、if文の条件にconst値を利用する必要がある
        • 例えば、final bool isTest = true;とし、if(isTest) 'Banana',とした場合は以下コンパイルエラー(const bool isTest = true;は可)
          • The values in a const set literal must be constants
void main() {
  final set = {
    'Cherry',
    'Litchi',
    if (true) 'Banana',
  };
  print(set);
}

実行結果

{Cherry, Litchi, Banana}
  • if文による追加:false版
void main() {
  final set = {
    'Cherry',
    'Litchi',
    if (false) 'Banana',
  };
  print(set);
}

実行結果

{Cherry, Litchi}
  • for文を利用した追加
    • 以下の場合は、setはSet<Object>となるためStringもintも格納可能
    • ※constのSetは作成不可
void main() {
  final set = {
    'Cherry',
    'Litchi',
    for (int i = 0; i < 3; i++) i,
  };
  print(set);
}

実行結果

[Cherry, Litchi, 0, 1, 2]
  • for文を利用した追加
    • Setの場合は、同じ値を指定数格納する役割に意味はない
void main() {
  final set = {
    'Cherry',
    'Litchi',
    for (int i = 0; i < 3; i++)'Banana',
  };
  print(set);
}

実行結果

{Cherry, Litchi, Banana}

簡単な個別確認

  • 添え字
    • 添え字での呼び出しに対応していない
    • elementAt関数で代用可能 ※値の格納は不可
void main() {
  final set = {'Apple', 'Cherry', 'Litchi'};
  print(set.elementAt(0));
  print(set.elementAt(1));
  print(set.elementAt(2));
}

実行結果

Apple
Cherry
Litchi
  • 中身をfor文で取得
void main() {
  final set = {'Apple', 'Cherry', 'Litchi'};
  for (var key in set) {
    print(key);
  }
}

実行結果

Apple
Cherry
Litchi
  • 中身をforEach文で取得
void main() {
  final set = {'Apple', 'Cherry', 'Litchi'};
  set.forEach(print);
}

実行結果

Apple
Cherry
Litchi

Growable(可変長と固定長)

  • lengthの変更(Growable = 可変長の確認)→不可
    • set.length = 3;でコンパイルエラー(Setには、lengthのsetterが無いので変更不可)
      • 中身をユニークに管理する必要があるため
void main() {
  final set = <String>{};
  print(set.length);
  set.length = 3;
  print(set.length);
  print(set);
}

実行結果

Compiler message:
lib/main.dart:4:7: Error: The setter 'length' isn't defined for the class 'Set<String>'.
 - 'Set' is from 'dart:core'.
Try correcting the name to the name of an existing setter, or defining a setter or field named 'length'.
  set.length = 3;
      ^^^^^^

便利なfactoryコンストラクタ

  • SetにはListに存在したような、List<E>.filledList<E>.unmodifiableList<E>.generateは無い
    • ※その他コンストラクタは存在

Map

定義(格納)

  • key値が重複した値を格納しようとすると無視され、一つ目のみ格納される
    • <int, String>は無くても可
void main() {
  var map = <int, String>{
    0: 'Apple',
    0: 'Apple',
    1: 'Strawberry',
    2: 'Peach',
    0: 'Apple',
  };
  print(map);
}

実行結果

{0: Apple, 1: Strawberry, 2: Peach}
  • 他のMap変数を追加
    • なお、key値が重複した値を格納しようとすると無視されるが、他のMap変数が優先される
void main() {
  var map1 = <int, String>{
    0: 'Apple',
    1: 'Strawberry',
    2: 'Peach',
  };
  var map2 = <int, String>{
    0: 'Apple',
    1: 'Cherry',
    2: 'Litchi',
    ...map1,
  };
  print(map2);
}

実行結果

{0: Apple, 1: Strawberry, 2: Peach}
  • 他のMap変数を追加
    • key値が重複していない値を格納する場合は、直観通りの順番となる
void main() {
  var map1 = <int, String>{
    0: 'Apple',
    1: 'Strawberry',
    2: 'Peach',
  };
  var map2 = <int, String>{
    3: 'Apple',
    4: 'Cherry',
    5: 'Litchi',
    ...map1,
  };
  print(map2);
}

実行結果

{3: Apple, 4: Cherry, 5: Litchi, 0: Apple, 1: Strawberry, 2: Peach}
  • 他のMap変数を追加
    • key値が重複していない値を格納する場合に、key値の順番を合わせる場合
void main() {
  var map1 = <int, String>{
    0: 'Apple',
    1: 'Strawberry',
    2: 'Peach',
  };
  var map2 = <int, String>{
    ...map1,
    3: 'Apple',
    4: 'Cherry',
    5: 'Litchi',
  };
  print(map2);
}

実行結果

{0: Apple, 1: Strawberry, 2: Peach, 3: Apple, 4: Cherry, 5: Litchi}
  • 空の場合は無視される
void main() {
  var map1 = <int, String>{};
  var map2 = <int, String>{
    0: 'Apple',
    1: 'Cherry',
    2: 'Litchi',
    ...map1,
  };
  print(map2);
}

実行結果

{0: Apple, 1: Cherry, 2: Litchi}
  • null時(map1 = null;)の場合はListと同様

    • nullの場合は実行時エラーとなる

    • null対策の?を付ける(...?map1,)ことで、無視される

  • if文による追加:true版

    • 注意
      • constのMapを作成する場合は、if文の条件にconst値を利用する必要がある
        • 例えば、final bool isTest = true;とし、if (isTest) 2: 'Banana',とした場合は以下コンパイルエラー(const bool isTest = true;は可)
          • The elements in a const map literal must be constant
void main() {
  var map = <int, String>{
    0: 'Cherry',
    1: 'Litchi',
    if (true) 2: 'Banana',
  };
  print(map);
}

実行結果

{0: Cherry, 1: Litchi, 2: Banana}
  • if文による追加:false版
void main() {
  const map = <int, String>{
    0: 'Cherry',
    1: 'Litchi',
    if (false) 2: 'Banana',
  };
  print(map);
}

実行結果

{0: Cherry, 1: Litchi}
  • for文を利用した追加
    • 簡単に同じ中身を指定数格納可能
    • ※constのListは作成不可
void main() {
  final map = <int, String>{
    0: 'Cherry',
    1: 'Litchi',
    for (int i = 2; i < 5; i++) i: 'Banana',
  };
  print(map);
}

実行結果

{0: Cherry, 1: Litchi, 2: Banana, 3: Banana, 4: Banana}

簡単な個別確認

  • 添え字(key値)
    • ※key値がStringのAの場合ならば、map['A']
void main() {
  var map = <int, String>{
    0: 'Apple',
    1: 'Cherry',
    2: 'Litchi',
  };
  print(map[0]);
  print(map[1]);
  print(map[2]);
}

実行結果

Apple
Cherry
Litchi
  • 中身をfor文で取得
void main() {
  var map = <int, String>{
    0: 'Apple',
    1: 'Cherry',
    2: 'Litchi',
  };
  print(map);

  for (var key in map.keys) {
    print(key);
  }

  for (var value in map.values) {
    print(value);
  }
}

実行結果

{0: Apple, 1: Cherry, 2: Litchi}
0
1
2
Apple
Cherry
Litchi
  • 中身をforEach文で取得
void main() {
  var map = <int, String>{
    0: 'Apple',
    1: 'Cherry',
    2: 'Litchi',
  };
  map.forEach((key, value) {
    print('$key:$value');
  });
}

実行結果

0:Apple
1:Cherry
2:Litchi

簡単な変更方法

  • 添え字
void main() {
  var map = <int, String>{
    0: 'Apple',
    1: 'Cherry',
    2: 'Litchi',
  };
  map[0] = 'Banana';
  map[3] = 'Banana';
  print(map);
}

実行結果

{0: Banana, 1: Cherry, 2: Litchi, 3: Banana}

Growable(可変長と固定長)

Setと同様

  • lengthの変更(Growable = 可変長の確認)→不可
    • map.length = 3;でコンパイルエラー(Mapには、lengthのsetterが無いので変更不可)
      • key値をユニークに管理する必要があるため
void main() {
  final map = <int, String>{};
  print(map.length);
  map.length = 3;
  print(map.length);
  print(map);
}

実行結果

Compiler message:
lib/main.dart:4:7: Error: The setter 'length' isn't defined for the class 'Map<int, String>'.
 - 'Map' is from 'dart:core'.
Try correcting the name to the name of an existing setter, or defining a setter or field named 'length'.
  map.length = 3;
      ^^^^^^

便利なfactoryコンストラクタ

  • MapにはListに存在したような、List<E>.filledList<E>.generateは無い

    • ※その他コンストラクタは存在
  • Map<K, V>.unmodifiable

    • Mapを引数にとり、不変のMapを作成
    • map2[0] = 'aaa';等変更しようとすると、実行時エラーとなる
      • Unhandled Exception: Unsupported operation: Cannot modify unmodifiable map
void main() {
  var map1 = <int, String>{
    0: 'Apple',
    1: 'Strawberry',
    2: 'Peach',
  };
  final map2 = Map<int, String>.unmodifiable(map1);
  print(map2);
}

実行結果

{0: Apple, 1: Strawberry, 2: Peach}

備考

  • Mapでは、もちろんList<String>も値として格納可能
void main() {
  var map = <String, List<String>>{
    'Fruit': ['Apple', 'Strawberry', 'Peach'],
    'Event': ['Olympic', 'World Cup', 'Paralympic Games'],
    'Anime': ['Pokémon', 'Doraemon', 'Dragon Ball'],
  };
  print(map);
}

実行結果

{Fruit: [Apple, Strawberry, Peach], Event: [Olympic, World Cup, Paralympic Games], Anime: [Pokémon, Doraemon, Dragon Ball]}
  • この例ではナンセンスだが、List<String>をkeyに可能
void main() {
  var map = <List<String>,String>{
    ['Apple', 'Strawberry', 'Peach']: 'Fruit',
  };
  print(map);
}

実行結果

{[Apple, Strawberry, Peach]: Fruit}
  • 存在しないkey値を参照した場合は、nullとなる
void main() {
  var map = <int, String>{
    0: 'Apple',
    1: 'Cherry',
    2: 'Litchi',
  };
  final test = map[100];
  print(test);
}

実行結果

null

  • Equatableにより、簡単に複数の変数を持つオブジェクトを比較( ==演算子や hashCodeを準備してくれる)※immutableクラスにのみ利用

  • 通常作成したクラスでは、中身が同じでも別オブジェクトのため、hashCodeは異なる

class User {
  final int id;
  final String username;
  final String email;
  User({this.id, this.username, this.email});
}

void main() {
  User user1 = User(id: 1, username: 'testuser', email: 'test@email.com');
  print(user1.hashCode);
  User user2 = User(id: 1, username: 'testuser', email: 'test@email.com');
  print(user2.hashCode);
}

実行結果

510021240
862784225
  • Equatableを利用すると、中身が同じであれば同じhashCodeとなる
    • propsは2つのインスタンスが等しいかどうかを判断するために使用されるプロパティのリスト
import 'package:equatable/equatable.dart';

class User extends Equatable {
  final int id;
  final String username;
  final String email;
  User({this.id, this.username, this.email});

  
  List<Object> get props => [id, username, email];
}

void main() {
  User user1 = User(id: 1, username: 'testuser', email: 'test@email.com');
  User user2 = User(id: 1, username: 'testuser', email: 'test@email.com');
  print(user1.props);
  print(user1.hashCode);
  print(user2.props);
  print(user2.hashCode);
}

実行結果

[1, testuser, test@email.com]
703299856
[1, testuser, test@email.com]
703299856
  • 一つでも値が異なれば、 hashCodeは変わる
import 'package:equatable/equatable.dart';

class User extends Equatable {
  final int id;
  final String username;
  final String email;
  User({this.id, this.username, this.email});

  
  List<Object> get props => [id, username, email];
}

void main() {
  User user1 = User(id: 1, username: 'testuser', email: 'test@email.com');
  User user2 = User(id: 2, username: 'testuser', email: 'test@email.com');
  print(user1.props);
  print(user1.hashCode);
  print(user2.props);
  print(user2.hashCode);
}

実行結果

[1, testuser, test@email.com]
71587939
[2, testuser, test@email.com]
268113581