- ジェネリックコレクション ページ一覧
HashSetとSortedSet
System.Collections.Generic.HashSetクラスとSystem.Collections.Generic.SortedSetクラスは、数学における集合の概念を表すコレクションクラスです。 HashSetは.NET Framework 3.5 SP1、SortedSetは.NET Framework 4より提供されています。
HashSetクラス・SortedSetクラスを使うことで、一方のコレクションが他方のコレクションと重複する部分を持つか、あるいは完全に異なるか、他方を内包するか、といったコレクション同士の包含関係を調べることができます。 (§.包含関係の検証) また、コレクション同士の和(OR)を求めて二つのコレクションをマージしたり、積(AND)を求めて共通する要素だけを残したりすることができます。 (§.集合演算)
HashSet・SortedSetでは、要素は重複することなく格納されます。 Listなどとは異なり既に追加されている要素を追加しようとしても重複して格納されることなく、常に単一の要素として扱われます。 そのため、HashSet・SortedSetはキーだけを扱う一種のDictionaryのようなコレクションと見ることもできます。 一方、Add/Remove/Clearなど要素を動的に追加・削除するメソッドや、foreachによる列挙などもサポートされているため、その点ではList等の他のコレクションと同様に扱うこともできます。 (§.基本操作)
SortedSetクラスは、名前のとおりHashSetクラスに並べ替えの機能を追加したクラスで、要素を追加したり列挙したりする際には自動的にソートされます。 ほかにも、集合内における最大値・最小値の取得ができるなど、SortedSetでのみ提供される機能も存在します。
.NET Framework 4以降ではHashSet・SortedSetはともにISet<T>インターフェイスを実装しています。 .NET Framework 3.5ではISet<T>は存在せず、そのためHashSetもISet<T>を実装していないので注意してください。 また.NET 5以降では、ISet<T>に対応する読み取り専用インターフェイスIReadOnlySet<T>インターフェイスが新たに導入されるため、HashSet・SortedSetを含むISetを読み取り専用で扱うことができるようになります。
System.Collections名前空間ではHashSetクラス・SortedSetクラスに相当するクラス、集合の概念を表す非ジェネリックなコレクションクラスは提供されていません。
基本操作
HashSetクラス・SortedSetクラスともに、Listなどの他のコレクションクラスと共通するAdd, Remove, Contains, Clearなどのメソッドが用意されています。 IEnumerable<T>インターフェイスを実装しているため、foreach/For Eachステートメントで要素を列挙することもできます。
そのため、重複する値が単一の要素として扱われる以外は、他のコレクションクラスと同様に扱うことができます。 また、SortedSetでは自動的に並べ替えが行われる点を除けば、動作と結果もHashSetと同じです。
using System;
using System.Collections.Generic;
class Sample {
static void Main()
{
var s = new HashSet<int>() {3, 1, 6, 4, 0};
Console.WriteLine(string.Join(", ", s));
// 要素を追加
s.Add(2);
Console.WriteLine(string.Join(", ", s));
// 要素を削除
s.Remove(6);
Console.WriteLine(string.Join(", ", s));
// 値5が含まれているか
Console.WriteLine("Contains 5? {0}", s.Contains(5));
}
}
Imports System
Imports System.Collections.Generic
Class Sample
Shared Sub Main()
Dim s As New HashSet(Of Integer) From {3, 1, 6, 4, 0}
Console.WriteLine(String.Join(", ", s))
' 要素を追加
s.Add(2)
Console.WriteLine(String.Join(", ", s))
' 要素を削除
s.Remove(6)
Console.WriteLine(String.Join(", ", s))
' 値5が含まれているか
Console.WriteLine("Contains 5? {0}", s.Contains(5))
End Sub
End Class
3, 1, 6, 4, 0 3, 1, 6, 4, 0, 2 3, 1, 4, 0, 2 Contains 5? False
using System;
using System.Collections.Generic;
class Sample {
static void Main()
{
var s = new SortedSet<int>() {3, 1, 6, 4, 0};
Console.WriteLine(string.Join(", ", s));
// 要素を追加
s.Add(2);
Console.WriteLine(string.Join(", ", s));
// 要素を削除
s.Remove(6);
Console.WriteLine(string.Join(", ", s));
// 値5が含まれているか
Console.WriteLine("Contains 5? {0}", s.Contains(5));
}
}
Imports System
Imports System.Collections.Generic
Class Sample
Shared Sub Main()
Dim s As New SortedSet(Of Integer) From {3, 1, 6, 4, 0}
Console.WriteLine(String.Join(", ", s))
' 要素を追加
s.Add(2)
Console.WriteLine(String.Join(", ", s))
' 要素を削除
s.Remove(6)
Console.WriteLine(String.Join(", ", s))
' 値5が含まれているか
Console.WriteLine("Contains 5? {0}", s.Contains(5))
End Sub
End Class
0, 1, 3, 4, 6 0, 1, 2, 3, 4, 6 0, 1, 2, 3, 4 Contains 5? False
列挙操作
HashSet・SortedSetともに、foreach/For Eachステートメントで要素を列挙することができます。 HashSetでは、Listと同様に要素を追加した順に列挙されますが、SortedSetでは値の大小関係に従って小さい順に列挙されます。
using System;
using System.Collections.Generic;
class Sample {
static void Main()
{
var s = new HashSet<int>() {3, 1, 6, 4, 0};
// 列挙して要素を表示
foreach (var e in s) {
Console.Write("{0}, ", e);
}
Console.WriteLine();
// 要素を追加
s.Add(2);
// 列挙して要素を表示
foreach (var e in s) {
Console.Write("{0}, ", e);
}
Console.WriteLine();
}
}
Imports System
Imports System.Collections.Generic
Class Sample
Shared Sub Main()
Dim s As New HashSet(Of Integer) From {3, 1, 6, 4, 0}
' 列挙して要素を表示
For Each e As Integer In s
Console.Write("{0}, ", e)
Next
Console.WriteLine()
' 要素を追加
s.Add(2)
' 列挙して要素を表示
For Each e As Integer In s
Console.Write("{0}, ", e)
Next
Console.WriteLine()
End Sub
End Class
3, 1, 6, 4, 0, 3, 1, 6, 4, 0, 2,
using System;
using System.Collections.Generic;
class Sample {
static void Main()
{
var s = new SortedSet<int>() {3, 1, 6, 4, 0};
// 列挙して要素を表示
foreach (var e in s) {
Console.Write("{0}, ", e);
}
Console.WriteLine();
// 要素を追加
s.Add(2);
// 列挙して要素を表示
foreach (var e in s) {
Console.Write("{0}, ", e);
}
Console.WriteLine();
}
}
Imports System
Imports System.Collections.Generic
Class Sample
Shared Sub Main()
Dim s As New SortedSet(Of Integer) From {3, 1, 6, 4, 0}
' 列挙して要素を表示
For Each e As Integer In s
Console.Write("{0}, ", e)
Next
Console.WriteLine()
' 要素を追加
s.Add(2)
' 列挙して要素を表示
For Each e As Integer In s
Console.Write("{0}, ", e)
Next
Console.WriteLine()
End Sub
End Class
0, 1, 3, 4, 6, 0, 1, 2, 3, 4, 6,
Reverseメソッドを使うと、逆順(値の大きい順)で列挙することができます。
重複する要素の追加
HashSet・SortedSetでは同じ値の要素を複数回格納しようとしても重複して格納されることはなく、常に1つだけが格納されます。 この点はDictionaryのキーと似た動作と言えますが、すでに存在する状態で追加しようとしても例外がスローされることはありません。
HashSet・SortedSetともに同じ要素が重複した状態になることはありませんが、HashSetでは格納した時の順序は維持されます(最初に格納された順になります)。
using System;
using System.Collections.Generic;
class Sample {
static void Main()
{
var s = new HashSet<int>();
// 同じ要素を複数回追加
// (同じ値がすでに存在する場合は、追加されない)
s.Add(3);
s.Add(3);
s.Add(1);
s.Add(1);
s.Add(2);
s.Add(3);
Console.WriteLine(string.Join(", ", s));
}
}
Imports System
Imports System.Collections.Generic
Class Sample
Shared Sub Main()
Dim s As New HashSet(Of Integer)()
' 同じ要素を複数回追加
' (同じ値がすでに存在する場合は、追加されない)
s.Add(3)
s.Add(3)
s.Add(1)
s.Add(1)
s.Add(2)
s.Add(3)
Console.WriteLine(String.Join(", ", s))
End Sub
End Class
3, 1, 2
using System;
using System.Collections.Generic;
class Sample {
static void Main()
{
var s = new SortedSet<int>();
// 同じ要素を複数回追加
// (同じ値がすでに存在する場合は、追加されない)
s.Add(3);
s.Add(3);
s.Add(1);
s.Add(1);
s.Add(2);
s.Add(3);
Console.WriteLine(string.Join(", ", s));
}
}
Imports System
Imports System.Collections.Generic
Class Sample
Shared Sub Main()
Dim s As New SortedSet(Of Integer)()
' 同じ要素を複数回追加
' (同じ値がすでに存在する場合は、追加されない)
s.Add(3)
s.Add(3)
s.Add(1)
s.Add(1)
s.Add(2)
s.Add(3)
Console.WriteLine(String.Join(", ", s))
End Sub
End Class
1, 2, 3
要素取得の試行・実際の値の取得 (TryGetValue)
HashSet・SortedSetに特定の要素が含まれているかどうかを調べるにはContainsメソッドを使うことができます。 このほかに、.NET Standard 2.1/.NET Framework 4.7.2/.NET Core 2.0以降では、TryGetValueメソッドを使うこともできます。
TryGetValueメソッドでは、HashSet・SortedSetに指定した値の要素があるかどうかを調べ、ある場合はその値を取得することができます。
using System;
using System.Collections.Generic;
class Sample {
static void Main()
{
var s = new HashSet<int>() {3, 1, 6, 4, 0};
// 値4が含まれているかContainsメソッドで調べる
Console.WriteLine(
"Contains 4? {0}",
s.Contains(4)
);
// 値4が含まれているかTryGetValueメソッドで調べる
// (含まれている場合、その値をoutパラメータで取得することもできる)
Console.WriteLine(
"TryGetValue 4? {0}",
s.TryGetValue(4, out var val)
);
}
}
Imports System
Imports System.Collections.Generic
Class Sample
Shared Sub Main()
Dim s As New HashSet(Of Integer) From {3, 1, 6, 4, 0}
' 値4が含まれているかContainsメソッドで調べる
Console.WriteLine(
"Contains 4? {0}",
s.Contains(4)
)
' 値4が含まれているかTryGetValueメソッドで調べる
' (含まれている場合、その値をoutパラメータで取得することもできる)
Dim val As Integer
Console.WriteLine(
"TryGetValue 4? {0}",
s.TryGetValue(4, val)
)
End Sub
End Class
Contains 4? True TryGetValue 4? True
using System;
using System.Collections.Generic;
class Sample {
static void Main()
{
var s = new SortedSet<int>() {3, 1, 6, 4, 0};
// 値4が含まれているかContainsメソッドで調べる
Console.WriteLine(
"Contains 4? {0}",
s.Contains(4)
);
// 値4が含まれているかTryGetValueメソッドで調べる
// (含まれている場合、その値をoutパラメータで取得することもできる)
Console.WriteLine(
"TryGetValue 4? {0}",
s.TryGetValue(4, out var val)
);
}
}
Imports System
Imports System.Collections.Generic
Class Sample
Shared Sub Main()
Dim s As New SortedSet(Of Integer) From {3, 1, 6, 4, 0}
' 値4が含まれているかContainsメソッドで調べる
Console.WriteLine(
"Contains 4? {0}",
s.Contains(4)
)
' 値4が含まれているかTryGetValueメソッドで調べる
' (含まれている場合、その値をoutパラメータで取得することもできる)
Dim val As Integer
Console.WriteLine(
"TryGetValue 4? {0}",
s.TryGetValue(4, val)
)
End Sub
End Class
Contains 4? True TryGetValue 4? True
ContainsメソッドとTryGetValueメソッドは、どちらもHashSet・SortedSet内に指定した値の要素があるかどうかを調べるという点では同じです。 一方TryGetValueメソッドは、要素がある場合には実際にHashSet・SortedSet内に格納されている値を取得できる点が異なります。
TryGetValueメソッドは、IEqualtyComparer<T>を指定してHashSetを構築した場合、またはIComparer<T>を指定してSortedSetを構築した場合などにおいて、単に値が含まれているかどうかだけでなく、実際に格納されている値を知りたい場合に有効に機能します。
例えば、大文字小文字の違いを無視するHashSet・SortedSetを構築したときに、HashSet・SortedSetにALICE
という値が含まれているか調べつつ、実際に格納されている値が何なのか(Alice
またはalice
、もしくはそれ以外なのか)を知りたい、といった場合には、ContainsメソッドではなくTryGetValueメソッドを使うことができます。
using System;
using System.Collections.Generic;
class Sample {
static void Main()
{
// 大文字小文字の違いを無視するHashSet
var caseInsensitiveSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase) {
"Alice", "Bob", "Charlie", "Dave", "Eve"
};
// 文字列"ALICE"が含まれているかどうか調べる
// 同時にHashSetに格納されている実際の綴りも取得する
var alice = "ALICE";
if (caseInsensitiveSet.TryGetValue(alice, out alice))
Console.WriteLine(alice);
// 文字列"cHArLIe"が含まれているかどうか調べる
// 同時にHashSetに格納されている実際の綴りも取得する
var charlie = "cHArLIe";
if (caseInsensitiveSet.TryGetValue(charlie, out charlie))
Console.WriteLine(charlie);
}
}
Imports System
Imports System.Collections.Generic
Class Sample
Shared Sub Main()
' 大文字小文字の違いを無視するHashSet
Dim caseInsensitiveSet As New HashSet(Of String)(StringComparer.OrdinalIgnoreCase) From {
"Alice", "Bob", "Charlie", "Dave", "Eve"
}
' 文字列"ALICE"が含まれているかどうか調べる
' 同時にHashSetに格納されている実際の綴りも取得する
Dim alice As String = "ALICE"
If caseInsensitiveSet.TryGetValue(alice, alice) Then
Console.WriteLine(alice)
End If
' 文字列"cHArLIe"が含まれているかどうか調べる
' 同時にHashSetに格納されている実際の綴りも取得する
Dim charlie As String = "cHArLIe"
If caseInsensitiveSet.TryGetValue(charlie, charlie) Then
Console.WriteLine(charlie)
End If
End Sub
End Class
Alice Charlie
using System;
using System.Collections.Generic;
class Sample {
static void Main()
{
// 大文字小文字の違いを無視するSortedSet
var caseInsensitiveSet = new SortedSet<string>(StringComparer.OrdinalIgnoreCase) {
"Alice", "Bob", "Charlie", "Dave", "Eve"
};
// 文字列"ALICE"が含まれているかどうか調べる
// 同時にSortedSetに格納されている実際の綴りも取得する
var alice = "ALICE";
if (caseInsensitiveSet.TryGetValue(alice, out alice))
Console.WriteLine(alice);
// 文字列"cHArLIe"が含まれているかどうか調べる
// 同時にSortedSetに格納されている実際の綴りも取得する
var charlie = "cHArLIe";
if (caseInsensitiveSet.TryGetValue(charlie, out charlie))
Console.WriteLine(charlie);
}
}
Imports System
Imports System.Collections.Generic
Class Sample
Shared Sub Main()
' 大文字小文字の違いを無視するSortedSet
Dim caseInsensitiveSet As New SortedSet(Of String)(StringComparer.OrdinalIgnoreCase) From {
"Alice", "Bob", "Charlie", "Dave", "Eve"
}
' 文字列"ALICE"が含まれているかどうか調べる
' 同時にSortedSetに格納されている実際の綴りも取得する
Dim alice As String = "ALICE"
If caseInsensitiveSet.TryGetValue(alice, alice) Then
Console.WriteLine(alice)
End If
' 文字列"cHArLIe"が含まれているかどうか調べる
' 同時にSortedSetに格納されている実際の綴りも取得する
Dim charlie As String = "cHArLIe"
If caseInsensitiveSet.TryGetValue(charlie, charlie) Then
Console.WriteLine(charlie)
End If
End Sub
End Class
Alice Charlie
HashSetにIEqualtyComparer<T>を指定して構築する例については§.HashSetでの等価性比較のカスタマイズ、SortedSetにIComparer<T>を指定して構築する例については§.SortedSetでの大小比較のカスタマイズを参照してください。
集合演算
HashSetクラス・SortedSetクラスには集合演算を行うメソッドとして次のメソッドが用意されています。 いずれも引数としてIEnumerable<T>を取り、戻り値はありません(void)。 したがって、これらのメソッドでは引数に指定された配列やコレクションなどとの演算結果をインスタンス自身に反映します。
メソッド | 動作 |
---|---|
HashSet.IntersectWith
SortedSet.IntersectWith |
現在の集合と引数で指定されたIEnumerable<T>との積集合を求める (引数の集合と共通する要素のみを残す) |
HashSet.UnionWith
SortedSet.UnionWith |
現在の集合と引数で指定されたIEnumerable<T>との和集合を求める (引数の集合の要素をマージする) |
HashSet.ExceptWith
SortedSet.ExceptWith |
現在の集合と引数で指定されたIEnumerable<T>との差集合を求める (引数の集合の要素を除外する) |
HashSet.SymmetricExceptWith
SortedSet.SymmetricExceptWith |
現在の集合と引数で指定されたIEnumerable<T>との対称差を求める (引数の集合と共通する要素のみを除外する) |
以下は、上記の集合演算を使った例です。
using System;
using System.Collections.Generic;
class Sample {
static void Main()
{
var set1 = new int[] {6, 2, 0, 4, 8};
var set2 = new int[] {3, 1, 2, 0, 4};
// 積集合を求める(IntersectWith)
var s1 = new HashSet<int>(set1);
s1.IntersectWith(set2);
Console.WriteLine(string.Join(", ", s1));
// 和集合を求める(UnionWith)
var s2 = new HashSet<int>(set1);
s2.UnionWith(set2);
Console.WriteLine(string.Join(", ", s2));
// 差集合を求める(ExceptWith)
var s3 = new HashSet<int>(set1);
s3.ExceptWith(set2);
Console.WriteLine(string.Join(", ", s3));
// 対象差を求める(SymmetricExceptWith)
var s4 = new HashSet<int>(set1);
s4.SymmetricExceptWith(set2);
Console.WriteLine(string.Join(", ", s4));
}
}
Imports System
Imports System.Collections.Generic
Class Sample
Shared Sub Main()
Dim set1() As Integer = New Integer() {6, 2, 0, 4, 8}
Dim set2() As Integer = New Integer() {3, 1, 2, 0, 4}
' 積集合を求める(IntersectWith)
Dim s1 As New HashSet(Of Integer)(set1)
s1.IntersectWith(set2)
Console.WriteLine(String.Join(", ", s1))
' 和集合を求める(UnionWith)
Dim s2 As New HashSet(Of Integer)(set1)
s2.UnionWith(set2)
Console.WriteLine(String.Join(", ", s2))
' 差集合を求める(ExceptWith)
Dim s3 As New HashSet(Of Integer)(set1)
s3.ExceptWith(set2)
Console.WriteLine(String.Join(", ", s3))
' 対象差を求める(SymmetricExceptWith)
Dim s4 As New HashSet(Of Integer)(set1)
s4.SymmetricExceptWith(set2)
Console.WriteLine(String.Join(", ", s4))
End Sub
End Class
2, 0, 4 6, 2, 0, 4, 8, 3, 1 6, 8 6, 8, 3, 1
using System;
using System.Collections.Generic;
class Sample {
static void Main()
{
var set1 = new int[] {6, 2, 0, 4, 8};
var set2 = new int[] {3, 1, 2, 0, 4};
// 積集合を求める(IntersectWith)
var s1 = new SortedSet<int>(set1);
s1.IntersectWith(set2);
Console.WriteLine(string.Join(", ", s1));
// 和集合を求める(UnionWith)
var s2 = new SortedSet<int>(set1);
s2.UnionWith(set2);
Console.WriteLine(string.Join(", ", s2));
// 差集合を求める(ExceptWith)
var s3 = new SortedSet<int>(set1);
s3.ExceptWith(set2);
Console.WriteLine(string.Join(", ", s3));
// 対象差を求める(SymmetricExceptWith)
var s4 = new SortedSet<int>(set1);
s4.SymmetricExceptWith(set2);
Console.WriteLine(string.Join(", ", s4));
}
}
Imports System
Imports System.Collections.Generic
Class Sample
Shared Sub Main()
Dim set1() As Integer = New Integer() {6, 2, 0, 4, 8}
Dim set2() As Integer = New Integer() {3, 1, 2, 0, 4}
' 積集合を求める(IntersectWith)
Dim s1 As New SortedSet(Of Integer)(set1)
s1.IntersectWith(set2)
Console.WriteLine(String.Join(", ", s1))
' 和集合を求める(UnionWith)
Dim s2 As New SortedSet(Of Integer)(set1)
s2.UnionWith(set2)
Console.WriteLine(String.Join(", ", s2))
' 差集合を求める(ExceptWith)
Dim s3 As New SortedSet(Of Integer)(set1)
s3.ExceptWith(set2)
Console.WriteLine(String.Join(", ", s3))
' 対象差を求める(SymmetricExceptWith)
Dim s4 As New SortedSet(Of Integer)(set1)
s4.SymmetricExceptWith(set2)
Console.WriteLine(String.Join(", ", s4))
End Sub
End Class
0, 2, 4 0, 1, 2, 3, 4, 6, 8 6, 8 1, 3, 6, 8
SortedSetで並べ替えが行われる以外は、結果は同じです。
以下の図は、上記のHashSet・SortedSetの集合演算メソッドの実行結果を図式化したものです。
LINQの拡張メソッドを使った集合演算
LINQの拡張メソッドを使うことでも、HashSet・SortedSetと同様の集合演算を行うことができます。 それぞれ対応するメソッドは次のとおりです。
演算 | HashSet/SortedSetのメソッド | LINQの拡張メソッド |
---|---|---|
積集合 | IntersectWith | Intersect |
和集合 | UnionWith | Union |
差集合 | ExceptWith | Except |
対象差 | SymmetricExceptWith | - |
以下の例はこれらのメソッドを使ってHashSet・SortedSetと同様の集合演算を行う例です。 LINQでは、SymmetricExceptWithに相当するような対象差を求めるメソッドは直接提供されませんが、以下の例のように差集合同士の和集合を求めることで対象差を求めることができます。
using System;
using System.Collections.Generic;
using System.Linq;
class Sample {
static void Main()
{
var set1 = new int[] {6, 2, 0, 4, 8};
var set2 = new int[] {3, 1, 2, 0, 4};
// 積集合を求める
Console.WriteLine(string.Join(", ", set1.Intersect(set2)));
// 和集合を求める
Console.WriteLine(string.Join(", ", set1.Union(set2)));
// 差集合を求める
Console.WriteLine(string.Join(", ", set1.Except(set2)));
// 対象差を求める
var diffset1 = set1.Except(set2);
var diffset2 = set2.Except(set1);
Console.WriteLine(string.Join(", ", diffset1.Union(diffset2)));
}
}
Imports System
Imports System.Collections.Generic
Imports System.Linq
Class Sample
Shared Sub Main()
Dim set1() As Integer = New Integer() {6, 2, 0, 4, 8}
Dim set2() As Integer = New Integer() {3, 1, 2, 0, 4}
' 積集合を求める
Console.WriteLine(String.Join(", ", set1.Intersect(set2)))
' 和集合を求める
Console.WriteLine(String.Join(", ", set1.Union(set2)))
' 差集合を求める
Console.WriteLine(String.Join(", ", set1.Except(set2)))
' 対象差を求める
Dim diffset1 As IEnumerable(Of Integer) = set1.Except(set2)
Dim diffset2 As IEnumerable(Of Integer) = set2.Except(set1)
Console.WriteLine(String.Join(", ", diffset1.Union(diffset2)))
End Sub
End Class
2, 0, 4, 6, 2, 0, 4, 8, 3, 1, 6, 8, 6, 8, 3, 1,
包含関係の検証
HashSetクラス・SortedSetクラスには包含関係の検証を行うメソッドとして次のメソッドが用意されています。 いずれも引数としてIEnumerable<T>を取り、戻り値は真偽値(boolean)です。 なお、要素の並びが異なっていても包含関係には影響しません。 これらのメソッドでは順序にかかわらずどのような要素が含まれているかどうかのみが検証されます。
メソッド | 動作 |
---|---|
HashSet.SetEquals
SortedSet.SetEquals |
現在の集合と引数で指定されたIEnumerable<T>が等しいかどうか調べる (現在の集合と引数の集合がすべて同じ要素で構成されている場合はtrue) |
HashSet.Overlaps
SortedSet.Overlaps |
現在の集合と引数で指定されたIEnumerable<T>が共通する要素を持つかどうか調べる (現在の集合と引数の集合が一つでも同じ要素を持つ場合はtrue) |
HashSet.IsSubsetOf
SortedSet.IsSubsetOf |
現在の集合が引数で指定されたIEnumerable<T>の部分集合かどうか調べる (現在の集合が引数の集合に含まれる場合はtrue) |
HashSet.IsProperSubsetOf
SortedSet.IsProperSubsetOf |
現在の集合が引数で指定されたIEnumerable<T>の真部分集合かどうか調べる (現在の集合が引数の集合に含まれ、かつ両者が等しくない場合はtrue) |
HashSet.IsSupersetOf
SortedSet.IsSupersetOf |
現在の集合が引数で指定されたIEnumerable<T>の上位集合かどうか調べる (現在の集合が引数の集合を含む場合はtrue) |
HashSet.IsProperSupersetOf
SortedSet.IsProperSupersetOf |
現在の集合が引数で指定されたIEnumerable<T>の真上位集合かどうか調べる (現在の集合が引数の集合を含み、かつ両者が等しくない場合はtrue) |
以下は、上記のメソッドを使った例です。 SortedSetでは並べ替えが行われる以外は、結果は同じです。
using System;
using System.Collections.Generic;
class Sample {
static void Main()
{
var s = new HashSet<int>() {3, 4, 1, 5, 2};
Console.WriteLine("Set: {{{0}}}", string.Join(", ", s));
Console.WriteLine();
// 2つの集合が完全に一致するか調べる (SetEquals)
Console.WriteLine(
"SetEquals {{1, 2, 3, 4, 5}}: {0}",
s.SetEquals(new[] {1, 2, 3, 4, 5})
);
Console.WriteLine(
"SetEquals {{1, 2, 3, 4, 6}}: {0}",
s.SetEquals(new[] {1, 2, 3, 4, 6})
);
//s 2つの集合が部分一致するか調べる (Overraps)
Console.WriteLine(
"Overraps {{2, 3, 5, 7}}: {0}",
s.Overlaps(new[] {2, 3, 5, 7})
);
Console.WriteLine(
"Overraps {{0, 6}}: {0}",
s.Overlaps(new[] {0, 6})
);
}
}
Imports System
Imports System.Collections.Generic
Class Sample
Shared Sub Main()
Dim s As New HashSet(Of Integer)() From {3, 4, 1, 5, 2}
Console.WriteLine("Set: {{{0}}}", String.Join(", ", s))
Console.WriteLine()
' 2つの集合が完全に一致するか調べる (SetEquals)
Console.WriteLine(
"SetEquals {{1, 2, 3, 4, 5}}: {0}",
s.SetEquals(New Integer() {1, 2, 3, 4, 5})
)
Console.WriteLine(
"SetEquals {{1, 2, 3, 4, 6}}: {0}",
s.SetEquals(New Integer() {1, 2, 3, 4, 6})
)
' 2つの集合が部分一致するか調べる (Overraps)
Console.WriteLine(
"Overraps {{2, 3, 5, 7}}: {0}",
s.Overlaps(New Integer() {2, 3, 5, 7})
)
Console.WriteLine(
"Overraps {{0, 6}}: {0}",
s.Overlaps(New Integer() {0, 6})
)
End Sub
End Class
Set: {3, 4, 1, 5, 2} SetEquals {1, 2, 3, 4, 5}: True SetEquals {1, 2, 3, 4, 6}: False Overraps {2, 3, 5, 7}: True Overraps {0, 6}: False
using System;
using System.Collections.Generic;
class Sample {
static void Main()
{
var s = new SortedSet<int>() {3, 4, 1, 5, 2};
Console.WriteLine("Set: {{{0}}}", string.Join(", ", s));
Console.WriteLine();
// 2つの集合が完全に一致するか調べる (SetEquals)
Console.WriteLine(
"SetEquals {{1, 2, 3, 4, 5}}: {0}",
s.SetEquals(new[] {1, 2, 3, 4, 5})
);
Console.WriteLine(
"SetEquals {{1, 2, 3, 4, 6}}: {0}",
s.SetEquals(new[] {1, 2, 3, 4, 6})
);
//s 2つの集合が部分一致するか調べる (Overraps)
Console.WriteLine(
"Overraps {{2, 3, 5, 7}}: {0}",
s.Overlaps(new[] {2, 3, 5, 7})
);
Console.WriteLine(
"Overraps {{0, 6}}: {0}",
s.Overlaps(new[] {0, 6})
);
}
}
Imports System
Imports System.Collections.Generic
Class Sample
Shared Sub Main()
Dim s As New SortedSet(Of Integer)() From {3, 4, 1, 5, 2}
Console.WriteLine("Set: {{{0}}}", String.Join(", ", s))
Console.WriteLine()
' 2つの集合が完全に一致するか調べる (SetEquals)
Console.WriteLine(
"SetEquals {{1, 2, 3, 4, 5}}: {0}",
s.SetEquals(New Integer() {1, 2, 3, 4, 5})
)
Console.WriteLine(
"SetEquals {{1, 2, 3, 4, 6}}: {0}",
s.SetEquals(New Integer() {1, 2, 3, 4, 6})
)
' 2つの集合が部分一致するか調べる (Overraps)
Console.WriteLine(
"Overraps {{2, 3, 5, 7}}: {0}",
s.Overlaps(New Integer() {2, 3, 5, 7})
)
Console.WriteLine(
"Overraps {{0, 6}}: {0}",
s.Overlaps(New Integer() {0, 6})
)
End Sub
End Class
Set: {1, 2, 3, 4, 5} SetEquals {1, 2, 3, 4, 5}: True SetEquals {1, 2, 3, 4, 6}: False Overraps {2, 3, 5, 7}: True Overraps {0, 6}: False
using System;
using System.Collections.Generic;
class Sample {
static void Main()
{
var s = new HashSet<int>() {3, 4, 1, 5, 2};
Console.WriteLine("Set: {{{0}}}", string.Join(", ", s));
Console.WriteLine();
// HashSetが引数の集合の部分集合かどうか調べる (IsSubsetOf)
Console.WriteLine(
"IsSubsetOf {{0, 1, 2, 3, 4, 5, 6}}: {0}",
s.IsSubsetOf(new[] {0, 1, 2, 3, 4, 5, 6})
);
// HashSetが引数の集合の真部分集合かどうか調べる (IsProperSubsetOf)
Console.WriteLine(
"IsProperSubsetOf {{0, 1, 2, 3, 4, 5, 6}}: {0}",
s.IsProperSubsetOf(new[] {0, 1, 2, 3, 4, 5, 6})
);
Console.WriteLine(
"IsSubsetOf {{1, 2, 3, 4, 5}}: {0}",
s.IsSubsetOf(new[] {1, 2, 3, 4, 5})
);
Console.WriteLine(
"IsProperSubsetOf {{1, 2, 3, 4, 5}}: {0}",
s.IsProperSubsetOf(new[] {1, 2, 3, 4, 5})
);
}
}
Imports System
Imports System.Collections.Generic
Class Sample
Shared Sub Main()
Dim s As New HashSet(Of Integer)() From {3, 4, 1, 5, 2}
Console.WriteLine("Set: {{{0}}}", String.Join(", ", s))
Console.WriteLine()
' HashSetが引数の集合の部分集合かどうか調べる (IsSubsetOf)
Console.WriteLine(
"IsSubsetOf {{0, 1, 2, 3, 4, 5, 6}}: {0}",
s.IsSubsetOf(New Integer() {0, 1, 2, 3, 4, 5, 6})
)
' HashSetが引数の集合の真部分集合かどうか調べる (IsProperSubsetOf)
Console.WriteLine(
"IsProperSubsetOf {{0, 1, 2, 3, 4, 5, 6}}: {0}",
s.IsProperSubsetOf(New Integer() {0, 1, 2, 3, 4, 5, 6})
)
Console.WriteLine(
"IsSubsetOf {{1, 2, 3, 4, 5}}: {0}",
s.IsSubsetOf(New Integer() {1, 2, 3, 4, 5})
)
Console.WriteLine(
"IsProperSubsetOf {{1, 2, 3, 4, 5}}: {0}",
s.IsProperSubsetOf(New Integer() {1, 2, 3, 4, 5})
)
End Sub
End Class
Set: {3, 4, 1, 5, 2} IsSubsetOf {0, 1, 2, 3, 4, 5, 6}: True IsProperSubsetOf {0, 1, 2, 3, 4, 5, 6}: True IsSubsetOf {1, 2, 3, 4, 5}: True IsProperSubsetOf {1, 2, 3, 4, 5}: False
using System;
using System.Collections.Generic;
class Sample {
static void Main()
{
var s = new SortedSet<int>() {3, 4, 1, 5, 2};
Console.WriteLine("Set: {{{0}}}", string.Join(", ", s));
Console.WriteLine();
// SortedSetが引数の集合の部分集合かどうか調べる (IsSubsetOf)
Console.WriteLine(
"IsSubsetOf {{0, 1, 2, 3, 4, 5, 6}}: {0}",
s.IsSubsetOf(new[] {0, 1, 2, 3, 4, 5, 6})
);
// SortedSetが引数の集合の真部分集合かどうか調べる (IsProperSubsetOf)
Console.WriteLine(
"IsProperSubsetOf {{0, 1, 2, 3, 4, 5, 6}}: {0}",
s.IsProperSubsetOf(new[] {0, 1, 2, 3, 4, 5, 6})
);
Console.WriteLine(
"IsSubsetOf {{1, 2, 3, 4, 5}}: {0}",
s.IsSubsetOf(new[] {1, 2, 3, 4, 5})
);
Console.WriteLine(
"IsProperSubsetOf {{1, 2, 3, 4, 5}}: {0}",
s.IsProperSubsetOf(new[] {1, 2, 3, 4, 5})
);
}
}
Imports System
Imports System.Collections.Generic
Class Sample
Shared Sub Main()
Dim s As New SortedSet(Of Integer)() From {3, 4, 1, 5, 2}
Console.WriteLine("Set: {{{0}}}", String.Join(", ", s))
Console.WriteLine()
' SortedSetが引数の集合の部分集合かどうか調べる (IsSubsetOf)
Console.WriteLine(
"IsSubsetOf {{0, 1, 2, 3, 4, 5, 6}}: {0}",
s.IsSubsetOf(New Integer() {0, 1, 2, 3, 4, 5, 6})
)
' SortedSetが引数の集合の真部分集合かどうか調べる (IsProperSubsetOf)
Console.WriteLine(
"IsProperSubsetOf {{0, 1, 2, 3, 4, 5, 6}}: {0}",
s.IsProperSubsetOf(New Integer() {0, 1, 2, 3, 4, 5, 6})
)
Console.WriteLine(
"IsSubsetOf {{1, 2, 3, 4, 5}}: {0}",
s.IsSubsetOf(New Integer() {1, 2, 3, 4, 5})
)
Console.WriteLine(
"IsProperSubsetOf {{1, 2, 3, 4, 5}}: {0}",
s.IsProperSubsetOf(New Integer() {1, 2, 3, 4, 5})
)
End Sub
End Class
Set: {1, 2, 3, 4, 5} IsSubsetOf {0, 1, 2, 3, 4, 5, 6}: True IsProperSubsetOf {0, 1, 2, 3, 4, 5, 6}: True IsSubsetOf {1, 2, 3, 4, 5}: True IsProperSubsetOf {1, 2, 3, 4, 5}: False
using System;
using System.Collections.Generic;
class Sample {
static void Main()
{
var s = new HashSet<int>() {3, 4, 1, 5, 2};
Console.WriteLine("Set: {{{0}}}", string.Join(", ", s));
Console.WriteLine();
// HashSetが引数の集合の上位集合かどうか調べる (IsSupersetOf)
Console.WriteLine(
"IsSupersetOf {{2, 3, 4}}: {0}",
s.IsSupersetOf(new[] {2, 3, 4})
);
// HashSetが引数の集合の真上位集合かどうか調べる (IsProperSupersetOf)
Console.WriteLine(
"IsProperSupersetOf {{2, 3, 4}}: {0}",
s.IsProperSupersetOf(new[] {2, 3, 4})
);
Console.WriteLine(
"IsSupersetOf {{1, 2, 3, 4, 5}}: {0}",
s.IsSupersetOf(new[] {1, 2, 3, 4, 5})
);
Console.WriteLine(
"IsProperSupersetOf {{1, 2, 3, 4, 5}}: {0}",
s.IsProperSupersetOf(new[] {1, 2, 3, 4, 5})
);
}
}
Imports System
Imports System.Collections.Generic
Class Sample
Shared Sub Main()
Dim s As New HashSet(Of Integer)() From {3, 4, 1, 5, 2}
Console.WriteLine("Set: {{{0}}}", String.Join(", ", s))
Console.WriteLine()
' HashSetが引数の集合の上位集合かどうか調べる (IsSupersetOf)
Console.WriteLine(
"IsSupersetOf {{2, 3, 4}}: {0}",
s.IsSupersetOf(New Integer() {2, 3, 4})
)
' HashSetが引数の集合の真上位集合かどうか調べる (IsProperSupersetOf)
Console.WriteLine(
"IsProperSupersetOf {{2, 3, 4}}: {0}",
s.IsProperSupersetOf(New Integer() {2, 3, 4})
)
Console.WriteLine(
"IsSupersetOf {{1, 2, 3, 4, 5}}: {0}",
s.IsSupersetOf(New Integer() {1, 2, 3, 4, 5})
)
Console.WriteLine(
"IsProperSupersetOf {{1, 2, 3, 4, 5}}: {0}",
s.IsProperSupersetOf(New Integer() {1, 2, 3, 4, 5})
)
End Sub
End Class
Set: {3, 4, 1, 5, 2} IsSupersetOf {2, 3, 4}: True IsProperSupersetOf {2, 3, 4}: True IsSupersetOf {1, 2, 3, 4, 5}: True IsProperSupersetOf {1, 2, 3, 4, 5}: False
using System;
using System.Collections.Generic;
class Sample {
static void Main()
{
var s = new SortedSet<int>() {3, 4, 1, 5, 2};
Console.WriteLine("Set: {{{0}}}", string.Join(", ", s));
Console.WriteLine();
// SortedSetが引数の集合の上位集合かどうか調べる (IsSupersetOf)
Console.WriteLine(
"IsSupersetOf {{2, 3, 4}}: {0}",
s.IsSupersetOf(new[] {2, 3, 4})
);
// SortedSetが引数の集合の真上位集合かどうか調べる (IsProperSupersetOf)
Console.WriteLine(
"IsProperSupersetOf {{2, 3, 4}}: {0}",
s.IsProperSupersetOf(new[] {2, 3, 4})
);
Console.WriteLine(
"IsSupersetOf {{1, 2, 3, 4, 5}}: {0}",
s.IsSupersetOf(new[] {1, 2, 3, 4, 5})
);
Console.WriteLine(
"IsProperSupersetOf {{1, 2, 3, 4, 5}}: {0}",
s.IsProperSupersetOf(new[] {1, 2, 3, 4, 5})
);
}
}
Imports System
Imports System.Collections.Generic
Class Sample
Shared Sub Main()
Dim s As New SortedSet(Of Integer)() From {3, 4, 1, 5, 2}
Console.WriteLine("Set: {{{0}}}", String.Join(", ", s))
Console.WriteLine()
' SortedSetが引数の集合の上位集合かどうか調べる (IsSupersetOf)
Console.WriteLine(
"IsSupersetOf {{2, 3, 4}}: {0}",
s.IsSupersetOf(New Integer() {2, 3, 4})
)
' SortedSetが引数の集合の真上位集合かどうか調べる (IsProperSupersetOf)
Console.WriteLine(
"IsProperSupersetOf {{2, 3, 4}}: {0}",
s.IsProperSupersetOf(New Integer() {2, 3, 4})
)
Console.WriteLine(
"IsSupersetOf {{1, 2, 3, 4, 5}}: {0}",
s.IsSupersetOf(New Integer() {1, 2, 3, 4, 5})
)
Console.WriteLine(
"IsProperSupersetOf {{1, 2, 3, 4, 5}}: {0}",
s.IsProperSupersetOf(New Integer() {1, 2, 3, 4, 5})
)
End Sub
End Class
Set: {1, 2, 3, 4, 5} IsSupersetOf {2, 3, 4}: True IsProperSupersetOf {2, 3, 4}: True IsSupersetOf {1, 2, 3, 4, 5}: True IsProperSupersetOf {1, 2, 3, 4, 5}: False
HashSetでの等価性比較のカスタマイズ
HashSetのコンストラクタで適切なIEqualityComparer<T>インターフェイスを指定することで、要素の等価性比較時の動作をカスタマイズ出来ます。 例えば、比較の際に大文字小文字の違いを無視するようにするといったことが出来ます。 以下は、StringComparerクラスを使って、大文字小文字の違いを無視するHashSetを作成する例です。
using System;
using System.Collections.Generic;
class Sample {
static void Main()
{
var names = new[] {"Alice", "Bob", "Charlie", "Dave", "Eve"};
// 大文字小文字の違いを意識するHashSet
var caseSensitiveSet = new HashSet<string>(names, StringComparer.Ordinal);
// 大文字小文字の違いを無視するHashSet
var caseInsensitiveSet = new HashSet<string>(names, StringComparer.OrdinalIgnoreCase);
Console.WriteLine(caseSensitiveSet.IsSupersetOf(new[] {"aLiCe", "BOB", "dave"}));
Console.WriteLine(caseInsensitiveSet.IsSupersetOf(new[] {"aLiCe", "BOB", "dave"}));
}
}
Imports System
Imports System.Collections.Generic
Class Sample
Shared Sub Main()
Dim names() As String = New String() {"Alice", "Bob", "Charlie", "Dave", "Eve"}
' 大文字小文字の違いを意識するHashSet
Dim caseSensitiveSet As New HashSet(Of String)(names, StringComparer.Ordinal)
' 大文字小文字の違いを無視するHashSet
Dim caseInsensitiveSet As New HashSet(Of String)(names, StringComparer.OrdinalIgnoreCase)
Console.WriteLine(caseSensitiveSet.IsSupersetOf(New String() {"aLiCe", "BOB", "dave"}))
Console.WriteLine(caseInsensitiveSet.IsSupersetOf(New String() {"aLiCe", "BOB", "dave"}))
End Sub
End Class
False True
IEqualityComparer<T>インターフェイスや実装例については等価性の定義と比較、StringComparerについては文字列と比較オプション・カルチャの並べ替え規則 §.StringComparison列挙型とStringComparerクラスを参照してください。
SortedSetでは等価性比較だけではなく大小関係の比較も行うため、IEqualityComparer<T>インターフェイスではなくIComparer<T>インターフェイスを要求します。
HashSetでKeyValuePairを扱う
等価性比較のカスタマイズ例として、HashSetでKeyValuePairを扱う例をみてみます。 次の例では、KeyValuePairからKeyの値を取得して比較を行うクラスKeyValuePairEqualityComparerを作成し、それをHashSetのコンストラクタに渡しています。 これによりKeyValuePair同士の比較ができるようになり、HashSetでKeyValuePairを扱うことができるようになります。
using System;
using System.Collections.Generic;
// KeyValuePair<string, int>の等価性比較を行うIEqualityComparer<T>
class KeyValuePairEqualityComparer : EqualityComparer<KeyValuePair<string, int>> {
public override bool Equals(KeyValuePair<string, int> x, KeyValuePair<string, int> y)
=> string.Equals(x.Key, y.Key); // KeyValuePairのKeyを比較する
public override int GetHashCode(KeyValuePair<string, int> obj)
=> obj.Key.GetHashCode(); // KeyValuePairのKeyからハッシュ値を取得する
}
class Sample {
static void Main()
{
var comparer = new KeyValuePairEqualityComparer();
var s1 = new HashSet<KeyValuePair<string, int>>(comparer);
s1.Add(KeyValuePair.Create("Dave", 1));
s1.Add(KeyValuePair.Create("Alice", 2));
s1.Add(KeyValuePair.Create("Alice", 99999));
s1.Add(KeyValuePair.Create("Bob", 3));
s1.Add(KeyValuePair.Create("Eve", 4));
s1.Add(KeyValuePair.Create("Charlie", 5));
Console.WriteLine(string.Join(", ", s1));
var s2 = new HashSet<KeyValuePair<string, int>>(comparer);
s2.Add(KeyValuePair.Create("Alice", 3));
s2.Add(KeyValuePair.Create("Bob", 1));
s2.Add(KeyValuePair.Create("Charlie", 2));
Console.WriteLine(string.Join(", ", s2));
// 差集合を求める
Console.WriteLine("[ExceptWith]");
s1.ExceptWith(s2);
Console.WriteLine(string.Join(", ", s1));
}
}
Imports System
Imports System.Collections.Generic
' KeyValuePair(Of String, Integer)の等価性比較を行うIEqualityComparer(Of T)
Class KeyValueEqualityComparer
Inherits EqualityComparer(Of KeyValuePair(Of String, Integer))
Public Overrides Function Equals(ByVal x As KeyValuePair(Of String, Integer), ByVal y As KeyValuePair(Of String, Integer)) As Boolean
' KeyValuePairのKeyを比較する
Return String.Equals(x.Key, y.Key)
End Function
Public Overrides Function GetHashCode(ByVal obj As KeyValuePair(Of String, Integer)) As Integer
' KeyValuePairのKeyからハッシュ値を取得する
Return obj.Key.GetHashCode()
End Function
End Class
Class Sample
Shared Sub Main()
Dim comparer As New KeyValueEqualityComparer()
Dim s1 As New HashSet(Of KeyValuePair(Of String, Integer))(comparer)
s1.Add(New KeyValuePair(Of String, Integer)("Dave", 1))
s1.Add(New KeyValuePair(Of String, Integer)("Alice", 2))
s1.Add(New KeyValuePair(Of String, Integer)("Alice", 99999))
s1.Add(New KeyValuePair(Of String, Integer)("Bob", 3))
s1.Add(New KeyValuePair(Of String, Integer)("Eve", 4))
s1.Add(New KeyValuePair(Of String, Integer)("Charlie", 5))
Console.WriteLine(String.Join(", ", s1))
Dim s2 As New HashSet(Of KeyValuePair(Of String, Integer))(comparer)
s2.Add(New KeyValuePair(Of String, Integer)("Alice", 3))
s2.Add(New KeyValuePair(Of String, Integer)("Bob", 1))
s2.Add(New KeyValuePair(Of String, Integer)("Charlie", 2))
Console.WriteLine(String.Join(", ", s2))
' 差集合を求める
Console.WriteLine("[ExceptWith]")
s1.ExceptWith(s2)
Console.WriteLine(String.Join(", ", s1))
End Sub
End Class
[Dave, 1], [Alice, 2], [Bob, 3], [Eve, 4], [Charlie, 5] [Alice, 3], [Bob, 1], [Charlie, 2] [ExceptWith] [Dave, 1], [Eve, 4]
この例のKeyValuePairEqualityComparerではKeyの値のみを比較しているため、Valueにどのような値が格納されているかといったことは一切考慮されません。 Keyの値が同一であれば、Valueの値に関わらず同一の要素とみなされます。
SortedSetでの大小比較のカスタマイズ
SortedSetのコンストラクタで適切なIComparer<T>インターフェイスを指定することで、要素の大小関係比較時の動作をカスタマイズ出来ます。 例えば、比較の際に大文字小文字の違いを無視するようにするといったことが出来ます。
以下は、大文字小文字を無視し、アルファベット順とは逆順(Z-Aの順)になるようにソートするIComparer<string>を実装し、SortedSetでの並べ替え順をカスタマイズする例です。
using System;
using System.Collections.Generic;
// 大文字小文字の違いを無視し、アルファベット順とは逆順にソートするためのIComparer
class CaseInsensitiveReverseStringComparer : IComparer<string> {
public int Compare(string x, string y)
// StringComparer.OrdinalIgnoreCase.Compareとは逆の結果を返すようにする
=> -1 * StringComparer.OrdinalIgnoreCase.Compare(x, y);
}
class Sample {
static void Main()
{
var names = new[] {"Alice", "Eve", "Charlie", "Bob", "Dave"};
var caseSensitiveSet = new SortedSet<string>(names, StringComparer.Ordinal);
var reverseCaseInsensitiveSet = new SortedSet<string>(names, new CaseInsensitiveReverseStringComparer());
Console.WriteLine("caseSensitiveSet");
Console.WriteLine(string.Join(", ", caseSensitiveSet));
Console.WriteLine(caseSensitiveSet.IsSupersetOf(new[] {"aLiCe", "BOB", "dave"}));
Console.WriteLine();
Console.WriteLine("reverseCaseInsensitiveSet");
Console.WriteLine(string.Join(", ", reverseCaseInsensitiveSet));
Console.WriteLine(reverseCaseInsensitiveSet.IsSupersetOf(new[] {"aLiCe", "BOB", "dave"}));
}
}
Imports System
Imports System.Collections.Generic
' 大文字小文字の違いを無視し、アルファベット順とは逆順にソートするためのIComparer
Class CaseInsensitiveReverseStringComparer
Implements IComparer(Of String)
Public Function Compare(ByVal x As String, ByVal y As String) As Integer Implements IComparer(Of String).Compare
' StringComparer.OrdinalIgnoreCase.Compareとは逆の結果を返すようにする
Return -1 * StringComparer.OrdinalIgnoreCase.Compare(x, y)
End Function
End Class
Class Sample
Shared Sub Main()
Dim names() As String = New String() {"Alice", "Bob", "Charlie", "Dave", "Eve"}
Dim caseSensitiveSet As New SortedSet(Of String)(names, StringComparer.Ordinal)
Dim reverseCaseInsensitiveSet As New SortedSet(Of String)(names, New CaseInsensitiveReverseStringComparer())
Console.WriteLine("caseSensitiveSet")
Console.WriteLine(String.Join(", ", caseSensitiveSet))
Console.WriteLine(caseSensitiveSet.IsSupersetOf(New String() {"aLiCe", "BOB", "dave"}))
Console.WriteLine()
Console.WriteLine("reverseCaseInsensitiveSet")
Console.WriteLine(String.Join(", ", reverseCaseInsensitiveSet))
Console.WriteLine(reverseCaseInsensitiveSet.IsSupersetOf(New String() {"aLiCe", "BOB", "dave"}))
End Sub
End Class
caseSensitiveSet Alice, Bob, Charlie, Dave, Eve False reverseCaseInsensitiveSet Eve, Dave, Charlie, Bob, Alice True
IComparer<T>インターフェイスや実装例については大小関係の定義と比較を参照してください。
文字列の大小関係、StringComparerについて詳しくは文字列と比較オプション・カルチャの並べ替え規則を参照してください。
SortedSetのみで提供される操作
以下の操作はHashSetには用意されておらず、SortedSetのみで提供される操作です。
最小値・最大値
SortedSetクラスでは、MinプロパティおよびMaxプロパティを参照することで、SortedSetの並べ替え順序での最小・最大の要素を取得することが出来ます。
using System;
using System.Collections.Generic;
class Sample {
static void Main()
{
var s = new SortedSet<string>() {"Alice", "Eve", "Charlie", "Bob", "Dave"};
Console.WriteLine(string.Join(", ", s));
// 最小値と最大値を表示
Console.WriteLine($"Mix: {s.Min}");
Console.WriteLine($"Max: {s.Max}");
}
}
Imports System
Imports System.Collections.Generic
Class Sample
Shared Sub Main()
Dim s As New SortedSet(Of String)() From {"Alice", "Eve", "Charlie", "Bob", "Dave"}
Console.WriteLine(String.Join(", ", s))
' 最小値と最大値を表示
Console.WriteLine($"Mix: {s.Min}")
Console.WriteLine($"Max: {s.Max}")
End Sub
End Class
Set: Alice, Bob, Charlie, Dave, Eve, Mix: Alice Max: Eve
SortedSetのコンストラクタでIComparer<T>を明示的に指定しない場合、最小値・最大値はデフォルトのソート順(大小関係)に従って求められます。 一方、コンストラクタでIComparer<T>を指定した場合は、そのIComparer<T>で定義される大小関係に従って最小値・最大値が求められることになります。
つまり、Minプロパティ・Maxプロパティは、IComparer<T>で定義されるソート順でSortedSetをソート(あるいは列挙)したときの、最初または最後の要素を取得するプロパティとなります。 このため、Minプロパティ・Maxプロパティで取得できる値が、常に値としての最小値・最大値になるとは限りません。
デフォルトとは逆順に並べ替えるIComparer<T>を指定してSortedSetを作成し、Minプロパティ・Maxプロパティが返す値の違いを見ると次のようになります。
using System;
using System.Collections.Generic;
class ReverseIntComparer : IComparer<int> {
public int Compare(int x, int y)
// intのデフォルトの大小関係とは逆の結果を返すようにする
=> -1 * Comparer<int>.Default.Compare(x, y);
}
class Sample {
static void Main()
{
// デフォルトの順序で並べ替えを行うSortedSet
var s1 = new SortedSet<int>() {0, 1, 2, 3, 4};
Console.WriteLine(string.Join(", ", s1));
// 最小値と最大値を表示
// (デフォルトのソート順での最初と最後の要素を表示する)
Console.WriteLine($"Mix: {s1.Min}");
Console.WriteLine($"Max: {s1.Max}");
Console.WriteLine();
// デフォルトとは逆の順序で並べ替えを行うSortedSet
var s2 = new SortedSet<int>(new ReverseIntComparer()) {0, 1, 2, 3, 4};
Console.WriteLine(string.Join(", ", s2));
// 最小値と最大値を表示
// (デフォルトとは逆のソート順での最初と最後の要素を表示する)
Console.WriteLine($"Mix: {s2.Min}");
Console.WriteLine($"Max: {s2.Max}");
}
}
Imports System
Imports System.Collections.Generic
Class ReverseIntegerComparer
Implements IComparer(Of Integer)
Public Function Compare(ByVal x As Integer, ByVal y As Integer) As Integer Implements IComparer(Of Integer).Compare
' Integerのデフォルトの大小関係とは逆の結果を返すようにする
Return -1 * Comparer(Of Integer).Default.Compare(x, y)
End Function
End Class
Class Sample
Shared Sub Main()
' デフォルトの順序で並べ替えを行うSortedSet
Dim s1 As New SortedSet(Of Integer)() From {0, 1, 2, 3, 4}
Console.WriteLine(String.Join(", ", s1))
' 最小値と最大値を表示
' (デフォルトのソート順での最初と最後の要素を表示する)
Console.WriteLine($"Mix: {s1.Min}")
Console.WriteLine($"Max: {s1.Max}")
Console.WriteLine()
' デフォルトとは逆の順序で並べ替えを行うSortedSet
Dim s2 As New SortedSet(Of Integer)(New ReverseIntegerComparer()) From {0, 1, 2, 3, 4}
Console.WriteLine(String.Join(", ", s2))
' 最小値と最大値を表示
' (デフォルトとは逆のソート順での最初と最後の要素を表示する)
Console.WriteLine($"Mix: {s2.Min}")
Console.WriteLine($"Max: {s2.Max}")
End Sub
End Class
0, 1, 2, 3, 4 Mix: 0 Max: 4 4, 3, 2, 1, 0 Mix: 4 Max: 0
HashSetでの最小値・最大値の取得
SortedSetと異なり、HashSetからは直接最小値・最大値を取得することはできませんが、LINQの拡張メソッドであるMinメソッドとMaxメソッドを使うことで求めることができます。
using System;
using System.Collections.Generic;
using System.Linq;
class Sample {
static void Main()
{
var s = new HashSet<string>() {"Alice", "Eve", "Charlie", "Bob", "Dave"};
// 最小値と最大値を表示
Console.WriteLine($"Mix: {s.Min()}");
Console.WriteLine($"Max: {s.Max()}");
}
}
Imports System
Imports System.Collections.Generic
Imports System.Linq
Class Sample
Shared Sub Main()
Dim s As New HashSet(Of String) From {"Alice", "Eve", "Charlie", "Bob", "Dave"}
' 最小値と最大値を表示
Console.WriteLine($"Mix: {s.Min()}")
Console.WriteLine($"Max: {s.Max()}")
End Sub
End Class
Mix: Alice Max: Eve
LINQのMinメソッド・Maxメソッドでは全要素を走査した上で最大・最小の要素を返します。 このメソッドをSortedSetに対しても用いることはできますが、無駄が多く、SortedListのMinプロパティ・Maxプロパティの代わりとしてMinメソッド・Maxメソッドを用いる利点はありません。
逆順での列挙
Reverseメソッドは、SortedSetを通常とは逆順に列挙する列挙子(IEnumerator<T>)を返します。 つまり、Reverseメソッドを使うと、SortedSet内の要素を逆の順序で列挙することができます。
Reverseメソッドは、Array.ReverseメソッドやList.Reverseメソッドとは異なり、SortedSet内の要素の並びを変更しない、非破壊的なメソッドです。 Reverseメソッドを呼び出してもSortedSet内の要素は逆順にはならず、あくまで逆順で列挙する列挙子を返すだけという点に注意してください。
using System;
using System.Collections.Generic;
class Sample {
static void Main()
{
var s = new SortedSet<int>() {5, 1, 0, 3, 4, 2};
// デフォルトの順序でSortedSetを列挙
foreach (var e in s) {
Console.Write("{0}, ", e);
}
Console.WriteLine();
// デフォルトとは逆順でSortedSetを列挙
foreach (var e in s.Reverse()) {
Console.Write("{0}, ", e);
}
Console.WriteLine();
}
}
Imports System
Imports System.Collections.Generic
Class Sample
Shared Sub Main()
Dim s As New SortedSet(Of Integer) From {5, 1, 0, 3, 4, 2}
' デフォルトの順序でSortedSetを列挙
For Each e As Integer In s
Console.Write("{0}, ", e)
Next
Console.WriteLine()
' デフォルトとは逆順でSortedSetを列挙
For Each e As Integer In s.Reverse()
Console.Write("{0}, ", e)
Next
Console.WriteLine()
End Sub
End Class
5, 4, 3, 2, 1, 0, 0, 1, 2, 3, 4, 5,
Reverseメソッドは列挙だけでなく、IEnumerator<T>を引数にとるメソッドやLINQのメソッドに渡して使うこともできます。
using System;
using System.Collections.Generic;
class Sample {
static void Main()
{
var s = new SortedSet<int>() {5, 1, 0, 3, 4, 2};
// デフォルトの順序でSortedSet内の要素を文字列として結合する
Console.WriteLine(string.Join(", ", s));
// デフォルトとは逆順でSortedSet内の要素を文字列として結合する
Console.WriteLine(string.Join(", ", s.Reverse()));
}
}
Imports System
Imports System.Collections.Generic
Class Sample
Shared Sub Main()
Dim s As New SortedSet(Of Integer) From {5, 1, 0, 3, 4, 2}
' デフォルトの順序でSortedSet内の要素を文字列として結合する
Console.WriteLine(String.Join(", ", s))
' デフォルトとは逆順でSortedSet内の要素を文字列として結合する
Console.WriteLine(String.Join(", ", s.Reverse()))
End Sub
End Class
0, 1, 2, 3, 4, 5 5, 4, 3, 2, 1, 0
using System;
using System.Collections.Generic;
using System.Linq;
class Sample {
static void Main()
{
var s = new SortedSet<int>() {5, 1, 0, 3, 4, 2};
// デフォルトの順序でSortedSet内の要素の一部分を取得する
// (最初の要素から1つスキップしたのち、3つ分を取得する)
Console.WriteLine(string.Join(", ", s.Skip(1).Take(3)));
// デフォルトとは逆順でSortedSet内の要素の一部分を取得する
// (最初の要素から1つスキップしたのち、3つ分を取得する)
Console.WriteLine(string.Join(", ", s.Reverse().Skip(1).Take(3)));
}
}
Imports System
Imports System.Collections.Generic
Imports System.Linq
Class Sample
Shared Sub Main()
Dim s As New SortedSet(Of Integer) From {5, 1, 0, 3, 4, 2}
' デフォルトの順序でSortedSet内の要素の一部分を取得する
' (最初の要素から1つスキップしたのち、3つ分を取得する)
Console.WriteLine(String.Join(", ", s.Skip(1).Take(3)))
' デフォルトとは逆順でSortedSet内の要素の一部分を取得する
' (最初の要素から1つスキップしたのち、3つ分を取得する)
Console.WriteLine(String.Join(", ", s.Reverse().Skip(1).Take(3)))
End Sub
End Class
1, 2, 3 4, 3, 2
部分集合(サブセット)の取得
GetViewBetweenメソッドを使うと、指定した範囲に該当する部分集合をSortedSet<T>として取得出来ます。
using System;
using System.Collections.Generic;
class Sample {
static void Main()
{
var s = new SortedSet<int>() {5, 1, 0, 3, 4, 2};
// 最小で1、最大で4の範囲に該当する部分集合を取得する
Console.WriteLine(string.Join(", ", s.GetViewBetween(1, 4)));
}
}
Imports System
Imports System.Collections.Generic
Class Sample
Shared Sub Main()
Dim s As New SortedSet(Of Integer) From {5, 1, 0, 3, 4, 2}
' 最小で1、最大で4の範囲に該当する部分集合を取得する
Console.WriteLine(String.Join(", ", s.GetViewBetween(1, 4)))
End Sub
End Class
1, 2, 3, 4
using System;
using System.Collections.Generic;
class Sample {
static void Main()
{
var s = new SortedSet<string>() {"Adams", "Cyndy", "Dave", "Bob", "Charlie", "Elliott", "Becky", "Alice", "Diana", "Eve"};
Console.Write("Set: ");
Console.WriteLine(string.Join(", ", s));
// 最小で"B"、最大で"E"の範囲に該当する部分集合を取得する
Console.Write("GetViewBetween(B, E): ");
Console.WriteLine(string.Join(", ", s.GetViewBetween("B", "E")));
// 最小で"Ae"、最大で"Bz"の範囲に該当する部分集合を取得する
Console.Write("GetViewBetween(Ae, Bz): ");
Console.WriteLine(string.Join(", ", s.GetViewBetween("Ae", "Bz")));
}
}
Imports System
Imports System.Collections.Generic
Class Sample
Shared Sub Main()
Dim s As New SortedSet(Of String) From {"Adams", "Cyndy", "Dave", "Bob", "Charlie", "Elliott", "Becky", "Alice", "Diana", "Eve"}
Console.Write("Set: ")
Console.WriteLine(String.Join(", ", s))
' 最小で"B"、最大で"E"の範囲に該当する部分集合を取得する
Console.Write("GetViewBetween(B, E): ")
Console.WriteLine(String.Join(", ", s.GetViewBetween("B", "E")))
' 最小で"Ae"、最大で"Bz"の範囲に該当する部分集合を取得する
Console.Write("GetViewBetween(Ae, Bz): ")
Console.WriteLine(String.Join(", ", s.GetViewBetween("Ae", "Bz")))
End Sub
End Class
Set: Adams, Alice, Becky, Bob, Charlie, Cyndy, Dave, Diana, Elliott, Eve GetViewBetween(B, E): Becky, Bob, Charlie, Cyndy, Dave, Diana GetViewBetween(Ae, Bz): Alice, Becky, Bob
部分集合のSortedSetと元のSortedSetに対する変更
GetViewBetweenメソッドは、引数で指定した範囲に該当する部分のビューを返します。 SortedSetの一部をコピーしたものが返されるわけではないため、元になったSortedSetに変更を加えると、GetViewBetweenメソッドで取得したサブセットにも反映されます。
using System;
using System.Collections.Generic;
class Sample {
static void Main()
{
var s = new SortedSet<string>() {"Alice", "Eve", "Charlie", "Bob", "Dave"};
Console.Write("s: ");
Console.WriteLine(string.Join(", ", s));
// 最小で"B"、最大で"E"の範囲に該当する部分集合を取得する
var view = s.GetViewBetween("B", "E");
Console.Write("view: ");
Console.WriteLine(string.Join(", ", view));
// 元の集合に変更を加える
// (上で取得した部分集合にも影響する)
s.Add("Diana");
Console.Write("view: ");
Console.WriteLine(string.Join(", ", view));
}
}
Imports System
Imports System.Collections.Generic
Class Sample
Shared Sub Main()
Dim s As New SortedSet(Of String)() From {"Alice", "Eve", "Charlie", "Bob", "Dave"}
Console.Write("s: ")
Console.WriteLine(String.Join(", ", s))
' 最小で"B"、最大で"E"の範囲に該当する部分集合を取得する
Dim view As SortedSet(Of String) = s.GetViewBetween("B", "E")
Console.Write("view: ")
Console.WriteLine(String.Join(", ", view))
' 元の集合に変更を加える
' (上で取得した部分集合にも影響する)
s.Add("Diana")
Console.Write("view: ")
Console.WriteLine(String.Join(", ", view))
End Sub
End Class
s: Alice, Bob, Charlie, Dave, Eve view: Bob, Charlie, Dave view: Bob, Charlie, Dave, Diana
また逆に、GetViewBetweenメソッドで取得したサブセットに変更を加えることもでき、この変更は元になったSortedSetにも反映されます。
using System;
using System.Collections.Generic;
class Sample {
static void Main()
{
var s = new SortedSet<string>() {"Alice", "Eve", "Charlie", "Bob", "Dave"};
Console.Write("s: ");
Console.WriteLine(string.Join(", ", s));
// 最小で"B"、最大で"E"の範囲に該当する部分集合を取得する
var view = s.GetViewBetween("B", "E");
Console.Write("view: ");
Console.WriteLine(string.Join(", ", view));
// 取得した部分集合に変更を加える
// (取得した部分集合の元になるSortedSetにも影響する)
view.Add("Cyndy");
Console.Write("s: ");
Console.WriteLine(string.Join(", ", s));
}
}
Imports System
Imports System.Collections.Generic
Class Sample
Shared Sub Main()
Dim s As New SortedSet(Of String)() From {"Alice", "Eve", "Charlie", "Bob", "Dave"}
Console.Write("s: ")
Console.WriteLine(String.Join(", ", s))
' 最小で"B"、最大で"E"の範囲に該当する部分集合を取得する
Dim view As SortedSet(Of String) = s.GetViewBetween("B", "E")
Console.Write("view: ")
Console.WriteLine(String.Join(", ", view))
' 取得した部分集合に変更を加える
' (取得した部分集合の元になるSortedSetにも影響する)
view.Add("Cyndy")
Console.Write("s: ")
Console.WriteLine(String.Join(", ", s))
End Sub
End Class
s: Alice, Bob, Charlie, Dave, Eve, s: Alice, Bob, Charlie, Cyndy, Dave, Eve,
SortedSetには、そのSortedSetがGetViewBetweenメソッドによって取得されたビューなのかどうかを判断するプロパティやメソッドは用意されていません。 そのため、GetViewBetweenメソッドによって取得したSortedSetに対して変更を行う際には、影響範囲に注意する必要があります。
.NET 5以降では、IReadOnlySet<T>インターフェイスが新たに導入されるため、GetViewBetweenメソッドで取得したSortedSetをIReadOnlySet<T>にキャストすることにより、読み取り専用のビューとして扱うことができるようになります。
GetViewBetweenメソッドで指定した範囲外の値をサブセットに対して追加しようとした場合、ArgumentOutOfRangeExceptionがスローされます。 GetViewBetweenメソッドで取得したサブセットに対しては、その範囲内に対する変更のみが行えます。
using System;
using System.Collections.Generic;
class Sample {
static void Main()
{
var s = new SortedSet<string>() {"Alice", "Eve", "Charlie", "Bob", "Dave"};
// 最小で"B"、最大で"E"の範囲に該当する部分集合を取得する
var view = s.GetViewBetween("B", "E");
// ビューの範囲外の値を追加しようとする
view.Add("Adams");
}
}
Imports System
Imports System.Collections.Generic
Class Sample
Shared Sub Main()
Dim s As New SortedSet(Of String)() From {"Alice", "Eve", "Charlie", "Bob", "Dave"}
' 最小で"B"、最大で"E"の範囲に該当する部分集合を取得する
Dim view As SortedSet(Of String) = s.GetViewBetween("B", "E")
' ビューの範囲外の値を追加しようとする
view.Add("Adams")
End Sub
End Class
Unhandled exception. System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values. (Parameter 'item') at System.Collections.Generic.SortedSet`1.TreeSubSet.AddIfNotPresent(T item) at System.Collections.Generic.SortedSet`1.Add(T item) at Sample.Main() in /home/smdn/samplecodes/dotnet/cs/test.cs:line 13
容量
HashSet・SortedSetでは、それ以上要素の追加や削除をする必要がなくなった場合に、TrimExcessメソッドを呼び出すことによって、HashSet・SortedSetが内部的に確保しているバッファを最小化することができ、不要な容量を減らすことができます。
また、HashSet・SortedSetに格納する最大要素数を事前に見積もれる場合は、EnsureCapacityメソッドを呼び出すことによって、あらかじめ指定したサイズのバッファを確保させておくことができます。 これにより、要素の追加に伴うバッファの再割当てとコピーを減らすことができます。 EnsureCapacityメソッドは、.NET Standard 2.1/.NET Core 2.1以降で利用できます。
容量と容量の縮小・確保について詳しくは、Listでの例を参照してください。