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

目次
- はじめに
- DartのHashSetから別のSetに含まれる要素を除いたSetを取得する
- differenceメソッドの戻り値
- 元のHashSetは変更されない
- differenceメソッドを使用する場合の注意点
- おわりに
1. はじめに
こんにちは、iOS のエディタアプリPWEditorとその後継PWEditorNextの開発者の二俣です。 2018年5月から毎日技術ブログを更新しています。
今回はDartのHashSetから、別のSetに含まれている要素を除いた新しいSetを取得するdifferenceメソッドについてです。
2. DartのHashSetから別のSetに含まれる要素を除いたSetを取得する
DartのHashSetでは、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', ]);
set1とset2の両方にFlutterが含まれています。
set1.difference(set2)を実行すると、set1の要素からset2にも含まれているFlutterが除かれ、DartとSwiftを含む新しい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に含まれている10、20、30、40のうち、set2にも含まれている20と40が除外されます。
そのため、結果には次の要素が含まれます。
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); }
この場合、set1のDartとFlutterはどちらもset2に含まれていないため、両方が結果に含まれます。
differenceメソッドの結果に要素が存在するかどうかは、isEmptyやisNotEmptyなどを使用して確認できます。
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にはDartとSwiftが含まれますが、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); }
Dartとdartは異なる文字列として扱われるため、set2にdartが含まれていても、set1のDartは除外されません。
そのため、結果にはDartとFlutterの両方が含まれます。
独自クラスを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}'); } }
set2のUserはnameが異なりますが、idが同じ場合は同じ要素として判定されます。
そのため、idが1と3の要素は除外され、結果にはidが2のUserが残ります。
2: Jiro
また、HashSetへ格納した後にhashCodeの計算に影響する値を変更すると、要素を正しく検索できなくなる可能性があります。
独自クラスをHashSetの要素として使用する場合は、==やhashCodeの判定に使用する値を、要素が格納されている間は変更しないようにすることが重要です。
なお、differenceメソッドはHashSet固有のメソッドではありません。
Setで定義されているメソッドなので、HashSet以外のSetでも利用できます。
変更前と変更後の項目を比較したり、ユーザーが選択していた項目のうち解除されたものを取得したり、2つのデータの差分を調べたりする場合などに利用できます。
リファレンス
6. おわりに
DartのHashSetから、別の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