プログラムを書こう!

実務や自作アプリ開発で習得した役に立つソフトウェア技術情報を発信するブログ

DartのHashSetから別のSetに含まれる要素を除いたSetを取得する

この記事は 2026 年 8 月 18 日に投稿しました。

目次

  1. はじめに
  2. DartのHashSetから別のSetに含まれる要素を除いたSetを取得する
  3. differenceメソッドの戻り値
  4. 元のHashSetは変更されない
  5. differenceメソッドを使用する場合の注意点
  6. おわりに

1. はじめに

こんにちは、iOS のエディタアプリPWEditorとその後継PWEditorNextの開発者の二俣です。 2018年5月から毎日技術ブログを更新しています。

今回はDartHashSetから、別のSetに含まれている要素を除いた新しいSetを取得するdifferenceメソッドについてです。

目次へ

2. DartのHashSetから別のSetに含まれる要素を除いたSetを取得する

DartHashSetでは、differenceメソッドを使用すると、元のSetには含まれていて、指定した別のSetには含まれていない要素を取得できます。

集合でいう「差集合」を取得するためのメソッドです。

differenceメソッドの形式は以下になります。

Set<E> difference(Set<Object?> other)

引数otherに、除外する要素を格納したSetを指定します。

例えば、次の2つのHashSetがあるとします。

final set1 = HashSet<String>();

set1.addAll([
  'Dart',
  'Flutter',
  'Swift',
]);

final set2 = HashSet<String>();

set2.addAll([
  'Flutter',
  'Kotlin',
]);

set1set2の両方にFlutterが含まれています。

set1.difference(set2)を実行すると、set1の要素からset2にも含まれているFlutterが除かれ、DartSwiftを含む新しいSetを取得できます。

実装例

import 'dart:collection';

void main() {
  final set1 = HashSet<String>();

  set1.addAll([
    'Dart',
    'Flutter',
    'Swift',
  ]);

  final set2 = HashSet<String>();

  set2.addAll([
    'Flutter',
    'Kotlin',
  ]);

  final result = set1.difference(set2);

  print(result);
}

取得されるresultには、次の要素が含まれます。

Dart
Swift

HashSetでは要素の順序は保証されないため、printで表示した場合の順序については注意してください。

重要なのは、

set1.difference(set2)

の場合、基準になるのはset1だということです。

set1の要素のうち、set2に含まれていない要素が結果として返されます。

そのため、set2だけに含まれているKotlinが結果に追加されることはありません。

例えば、整数でも同じように使用できます。

import 'dart:collection';

void main() {
  final set1 = HashSet<int>();

  set1.addAll([
    10,
    20,
    30,
    40,
  ]);

  final set2 = HashSet<int>();

  set2.addAll([
    20,
    40,
    50,
  ]);

  final result = set1.difference(set2);

  print(result);
}

set1に含まれている10203040のうち、set2にも含まれている2040が除外されます。

そのため、結果には次の要素が含まれます。

10
30

このように、differenceメソッドを使用すると、2つのSetを比較して、一方にだけ含まれている要素を簡単に取得できます。

目次へ

3. differenceメソッドの戻り値

differenceメソッドの戻り値はSet<E>です。

boolなどの判定結果ではなく、差分となる要素を格納した新しいSetが返されます。

例えば、次のようになります。

import 'dart:collection';

void main() {
  final set1 = HashSet<int>();

  set1.addAll([
    10,
    20,
    30,
    40,
  ]);

  final set2 = HashSet<int>();

  set2.addAll([
    20,
    40,
  ]);

  final difference = set1.difference(set2);

  print(difference);
}

differenceには、set2に含まれていないset1の要素が格納されます。

10
30

また、2つのSetに同じ要素がすべて含まれている場合は、結果は空のSetになります。

import 'dart:collection';

void main() {
  final set1 = HashSet<String>();

  set1.addAll([
    'Dart',
    'Flutter',
  ]);

  final set2 = HashSet<String>();

  set2.addAll([
    'Dart',
    'Flutter',
  ]);

  final difference = set1.difference(set2);

  print(difference);
  print(difference.isEmpty);
}

set1のすべての要素がset2にも含まれているため、差分となる要素はありません。

そのため、

difference.isEmpty

trueになります。

逆に、共通する要素が1つもない場合は、元のSetのすべての要素が結果に含まれます。

import 'dart:collection';

void main() {
  final set1 = HashSet<String>();

  set1.addAll([
    'Dart',
    'Flutter',
  ]);

  final set2 = HashSet<String>();

  set2.addAll([
    'Swift',
    'Kotlin',
  ]);

  final difference = set1.difference(set2);

  print(difference);
}

この場合、set1DartFlutterはどちらもset2に含まれていないため、両方が結果に含まれます。

differenceメソッドの結果に要素が存在するかどうかは、isEmptyisNotEmptyなどを使用して確認できます。

final difference = set1.difference(set2);

if (difference.isNotEmpty) {
  print('差分があります');
}

このように、2つのSetの差分を取得した後、その差分の有無によって処理を分けることもできます。

目次へ

4. 元のHashSetは変更されない

differenceメソッドを使用する際に重要なのは、元のHashSetから要素が削除されるわけではないという点です。

differenceメソッドは、差分となる要素を格納した新しいSetを返します。

例えば、次のように確認できます。

import 'dart:collection';

void main() {
  final set1 = HashSet<String>();

  set1.addAll([
    'Dart',
    'Flutter',
    'Swift',
  ]);

  final set2 = HashSet<String>();

  set2.addAll([
    'Flutter',
  ]);

  final difference = set1.difference(set2);

  print(set1);
  print(difference);
}

differenceにはDartSwiftが含まれますが、set1からFlutterが削除されるわけではありません。

set1には引き続き、

Dart
Flutter
Swift

の3つの要素が含まれています。

元のHashSet自体から、別のSetに含まれている要素を削除したい場合は、removeAllメソッドを使用できます。

set1.removeAll(set2);

この場合は、set1自体が変更されます。

一方、

final difference = set1.difference(set2);

では、set1を変更せずに差分を取得できます。

そのため、元のデータを残したまま2つの集合を比較したい場合はdifferenceメソッドが便利です。

例えば、変更前と変更後の項目を比較する場合にも利用できます。

final oldItems = <String>{
  'Dart',
  'Flutter',
  'Swift',
};

final newItems = <String>{
  'Dart',
  'Flutter',
  'Kotlin',
};

final removedItems = oldItems.difference(newItems);
final addedItems = newItems.difference(oldItems);

print(removedItems);
print(addedItems);

removedItemsには、変更前には存在したものの変更後には存在しなくなった、

Swift

が含まれます。

一方、addedItemsには、新しく追加された、

Kotlin

が含まれます。

このようにdifferenceメソッドは、2つのデータの追加・削除された要素を調べる場合にも利用できます。

目次へ

5. differenceメソッドを使用する場合の注意点

differenceメソッドを使用する場合は、どちらのSetを基準にするかに注意が必要です。

例えば、

final set1 = <String>{
  'Dart',
  'Flutter',
  'Swift',
};

final set2 = <String>{
  'Flutter',
  'Kotlin',
};

という2つのSetがある場合、

final result1 = set1.difference(set2);

と、

final result2 = set2.difference(set1);

では結果が異なります。

set1.difference(set2)では、set1にあってset2にはない要素が取得されるため、

Dart
Swift

が結果に含まれます。

一方、

set2.difference(set1)

では、set2にあってset1にはない要素が取得されるため、

Kotlin

が結果に含まれます。

つまり、

set1.difference(set2)

と、

set2.difference(set1)

は同じ結果になるとは限りません。

2つのSetのどちらか一方にだけ存在するすべての要素を取得したい場合は、それぞれの差集合を取得して組み合わせる方法があります。

final onlySet1 = set1.difference(set2);
final onlySet2 = set2.difference(set1);

final difference = {
  ...onlySet1,
  ...onlySet2,
};

print(difference);

この場合、両方に含まれているFlutterを除いた、

Dart
Swift
Kotlin

が結果に含まれます。

また、HashSetでの要素の判定は、HashSetが使用している等価性の判定に基づいて行われます。

通常のHashSetでは、オブジェクトの==演算子とhashCodeが要素の判定に使用されます。

例えば、文字列の場合は大文字と小文字が区別されます。

import 'dart:collection';

void main() {
  final set1 = HashSet<String>();

  set1.addAll([
    'Dart',
    'Flutter',
  ]);

  final set2 = HashSet<String>();

  set2.addAll([
    'dart',
  ]);

  final difference = set1.difference(set2);

  print(difference);
}

Dartdartは異なる文字列として扱われるため、set2dartが含まれていても、set1Dartは除外されません。

そのため、結果にはDartFlutterの両方が含まれます。

独自クラスをHashSetの要素として使用する場合は、==演算子とhashCodeの実装も重要になります。

例えば、次のようなクラスを使用します。

class User {
  final int id;
  final String name;

  User(this.id, this.name);

  @override
  bool operator ==(Object other) {
    return other is User && other.id == id;
  }

  @override
  int get hashCode => id.hashCode;
}

このクラスでは、idが同じであれば同じUserとして扱われます。

import 'dart:collection';

void main() {
  final set1 = HashSet<User>();

  set1.addAll([
    User(1, 'Taro'),
    User(2, 'Jiro'),
    User(3, 'Saburo'),
  ]);

  final set2 = HashSet<User>();

  set2.addAll([
    User(1, 'Ichiro'),
    User(3, 'Shiro'),
  ]);

  final difference = set1.difference(set2);

  for (final user in difference) {
    print('${user.id}: ${user.name}');
  }
}

set2Usernameが異なりますが、idが同じ場合は同じ要素として判定されます。

そのため、id13の要素は除外され、結果にはid2Userが残ります。

2: Jiro

また、HashSetへ格納した後にhashCodeの計算に影響する値を変更すると、要素を正しく検索できなくなる可能性があります。

独自クラスをHashSetの要素として使用する場合は、==hashCodeの判定に使用する値を、要素が格納されている間は変更しないようにすることが重要です。

なお、differenceメソッドはHashSet固有のメソッドではありません。

Setで定義されているメソッドなので、HashSet以外のSetでも利用できます。

変更前と変更後の項目を比較したり、ユーザーが選択していた項目のうち解除されたものを取得したり、2つのデータの差分を調べたりする場合などに利用できます。

リファレンス

Dart

DartPad

HashSet

Set

Set.difference

目次へ

6. おわりに

DartHashSetから、別のSetに含まれている要素を除いた新しいSetを取得するには、differenceメソッドを使用します。

例えば、

final set1 = <String>{
  'Dart',
  'Flutter',
  'Swift',
};

final set2 = <String>{
  'Flutter',
  'Kotlin',
};

final difference = set1.difference(set2);

とすると、set1に含まれていてset2には含まれていない、

Dart
Swift

differenceに含まれます。

differenceメソッドの戻り値はSet<E>で、元のSetを変更せずに差分となる要素を取得できます。

そのため、

final removedItems = oldItems.difference(newItems);

のようにすると、以前は存在していたものの新しいデータでは存在しなくなった要素を取得できます。

逆に、

final addedItems = newItems.difference(oldItems);

とすると、新しく追加された要素を取得できます。

ただし、

set1.difference(set2)

ではset1が基準となり、

set2.difference(set1)

ではset2が基準となるため、呼び出す順番によって結果が異なる点には注意が必要です。

また、HashSetでは、通常==演算子とhashCodeを使用して要素が同じかどうかを判定するため、独自クラスを要素として使用する場合は、これらを適切に実装することも重要です。

2つの集合を比較して一方にだけ存在する要素を簡潔に取得できるため、変更前後のデータ比較や、追加・削除された項目の確認などに利用できるメソッドです。

目次へ


私が技術者として所属している ニューラル では、 組み込み・制御系ソフトウェアを中心に、モバイルアプリや業務アプリなど、さまざまなソフトウェアの開発を行っています。

組み込み分野では、リアルタイムOSを利用したシステム開発をはじめ、弊社製品のハイブリッドOS Bi-OS など、長年培ってきた組み込み・制御系の技術を活かした開発に取り組んでいます。

また、iOS(Swift/SwiftUI)やWindows(C#)をはじめとしたアプリケーション開発、Webシステムの開発などにも対応しています。

組み込みソフトウェア、モバイルアプリ、Windowsアプリ、Webシステムなど、ソフトウェア開発に関するご相談がございましたら、お気軽にお問い合わせください。

また、ニューラルでは一緒にソフトウェア開発に取り組んでいただける技術者の方も募集しています。
ご興味がございましたら、お気軽にお問い合わせください。

EMAIL : info-nr@newral.co.jp / m-futamata@newral.co.jp
TEL : 042-523-3663
FAX : 042-540-1688

目次へ