ここでは、2つの配列(またはコレクション)を比べて、両者の要素がすべて同じか調べる方法を紹介します。
まず思いつくのは、For文を使って1つ1つの要素が等しいかを調べる方法です。例えば、以下のような方法です。
'比較する2つの配列 Dim ary1 As String() = New String() {"いち", "に", "さん"} Dim ary2 As String() = New String() {"いち", "に", "さん"} '結果を格納する変数 Dim isEqual As Boolean = True If Object.ReferenceEquals(ary1, ary2) Then '同一のインスタンスの時は、同じとする isEqual = True ElseIf ary1 Is Nothing OrElse ary2 Is Nothing _ OrElse ary1.Length <> ary2.Length Then 'どちらかがNULLか、要素数が異なる時は、同じではない isEqual = False Else '1つ1つの要素が等しいかを調べる Dim i As Integer For i = 0 To ary1.Length - 1 'ary1の要素のEqualsメソッドで、ary2の要素と等しいか調べる If Not ary1(i).Equals(ary2(i)) Then '1つでも等しくない要素があれば、同じではない isEqual = False Exit For End If Next End If '結果を表示する If isEqual Then Console.WriteLine("2つの配列は等しいです") Else Console.WriteLine("2つの配列は等しくありません") End If
//比較する2つの配列 string[] ary1 = new string[] { "いち", "に", "さん" }; string[] ary2 = new string[] { "いち", "に", "さん" }; //結果を格納する変数 bool isEqual = true; if (object.ReferenceEquals(ary1, ary2)) { //同一のインスタンスの時は、同じとする isEqual = true; } else if (ary1 == null || ary2 == null || ary1.Length != ary2.Length) { //どちらかがNULLか、要素数が異なる時は、同じではない isEqual = false; } else { //1つ1つの要素が等しいかを調べる for (int i = 0; i < ary1.Length; i++) { //ary1の要素のEqualsメソッドで、ary2の要素と等しいか調べる if (!ary1[i].Equals(ary2[i])) { //1つでも等しくない要素があれば、同じではない isEqual = false; break; } } } //結果を表示する if (isEqual) { Console.WriteLine("2つの配列は等しいです"); } else { Console.WriteLine("2つの配列は等しくありません"); }
.NET Framework 3.5以降でLINQを使えるのであれば、Enumerable.SequenceEqualメソッドでできます。
'比較する2つの配列(コレクションでも可) Dim ary1 As String() = New String() {"いち", "に", "さん"} Dim ary2 As String() = New String() {"いち", "に", "さん"} '2つのシーケンスが等しいか調べる Dim isEqual As Boolean = ary1.SequenceEqual(ary2) '結果を表示する If isEqual Then Console.WriteLine("2つの配列は等しいです") Else Console.WriteLine("2つの配列は等しくありません") End If
//比較する2つの配列(コレクションでも可) string[] ary1 = new string[] { "いち", "に", "さん" }; string[] ary2 = new string[] { "いち", "に", "さん" }; //2つのシーケンスが等しいか調べる bool isEqual = ary1.SequenceEqual(ary2); //結果を表示する if (isEqual) { Console.WriteLine("2つの配列は等しいです"); } else { Console.WriteLine("2つの配列は等しくありません"); }
上の例のようにSequenceEqualメソッドを使用すると、Equalsメソッド(EqualityComparer"T".Default)を使って要素を比較します。要素を比較する方法を変更するには、SequenceEqualメソッドの2番目の引数にIEqualityComparerを指定します。IEqualityComparerについては「IEqualityComparerを指定して、一致する条件を変更する」で説明していますので、そちらをご覧ください。
.NET Framework 4.0以降で配列を比較するのであれば、IStructuralEquatable.Equalsメソッドを使う方法もあります。
このメソッドも2番目の引数にIEqualityComparerを渡して、要素を比較する方法を指定します。下の例のように、ここにStructuralComparisons.StructuralEqualityComparerを指定すると、要素をIStructuralEquatableにキャストできる場合はIStructuralEquatable.Equalsで、できない場合は要素のEqualsで比較します。つまり、例えば要素が配列だった場合は、その配列内の要素も比較されます。
'Imports System.Collections.Generic '比較する2つの配列 Dim ary1 As String() = New String() {"いち", "に", "さん"} Dim ary2 As String() = New String() {"いち", "に", "さん"} '2つの配列が等しいか調べる Dim isEqual As Boolean = DirectCast(ary1, IStructuralEquatable).Equals( _ ary2, StructuralComparisons.StructuralEqualityComparer) '結果を表示する If isEqual Then Console.WriteLine("2つの配列は等しいです") Else Console.WriteLine("2つの配列は等しくありません") End If
//using System.Collections; //比較する2つの配列 string[] ary1 = new string[] { "いち", "に", "さん" }; string[] ary2 = new string[] { "いち", "に", "さん" }; //2つの配列が等しいか調べる bool isEqual = ((IStructuralEquatable)ary1).Equals(ary2, StructuralComparisons.StructuralEqualityComparer); //結果を表示する if (isEqual) { Console.WriteLine("2つの配列は等しいです"); } else { Console.WriteLine("2つの配列は等しくありません"); }
J#のライブラリ「vjslib.dll」にはArrays.equalsメソッドというものがあり、これを使って2つの配列を比較できます。 Arrays.equalsメソッドには幾つかのオーバーロードがあり、Byte、Int16、Int32、Int64、Single、Double、Boolean、Char、Object型の配列を比較できます。なおこの方法は32ビット環境でのみ使用できるようです。
もしvjslib.dllがなければ、Microsoft Visual J# 再頒布可能パッケージをインストールする必要があります。
以下のサンプルを実行するには、参照設定に「vjslib.dll」を追加する必要があります。
'比較する2つの配列 Dim ary1 As Integer() = New Integer() {0, 1, 2, 3} Dim ary2 As Integer() = New Integer() {0, 1, 2, 3} '2つの配列が等しいか調べる Dim isEqual As Boolean = java.util.Arrays.equals(ary1, ary2) '結果を表示する If isEqual Then Console.WriteLine("2つの配列は等しいです") Else Console.WriteLine("2つの配列は等しくありません") End If
//比較する2つの配列 int[] ary1 = new int[] { 0, 1, 2, 3 }; int[] ary2 = new int[] { 0, 1, 2, 3 }; //2つの配列が等しいか調べる bool isEqual = java.util.Arrays.equals(ary1, ary2); //結果を表示する if (isEqual) { Console.WriteLine("2つの配列は等しいです"); } else { Console.WriteLine("2つの配列は等しくありません"); }
バイト型配列を比較する場合は、さらに高速な方法があります。その一つが、アンセーフコードを使用する方法です。
「Uhuru Mkate: Byte Array Comparison Benchmarks」に多くの方法が紹介されていますが、その中で「unsafeLongCompare」とされている方法(元記事は、「performance - C# byte[] comparison without bound checks - Stack Overflow」)を以下に紹介させていただきます。
なおアンセーフコードはVB.NETではサポートされていません。また、プロジェクトのプロパティで「ビルド」-「アンセーフコードの許可」を有効にする必要があります。
public static bool ArrayCompare64(byte[] a, byte[] b) { if (object.ReferenceEquals(a, b)) { return true; } if (a == null || b == null || a.Length != b.Length) { return false; } int len = a.Length; unsafe { fixed (byte* ap = a, bp = b) { long* alp = (long*)ap; long* blp = (long*)bp; for (; len >= 8; len -= 8) { if (*alp != *blp) { return false; } alp++; blp++; } byte* ap2 = (byte*)alp; byte* bp2 = (byte*)blp; for (; len > 0; len--) { if (*ap2 != *bp2) { return false; } ap2++; bp2++; } } } return true; }
同じくバイト型配列を高速で比較する方法として、Microsoft Visual C ランタイムライブラリ(msvcrt.dll)のmemcmpを使う方法もあります。
<System.Runtime.InteropServices.DllImport("msvcrt.dll", _ CallingConvention:=System.Runtime.InteropServices.CallingConvention.Cdecl)> _ Private Shared Function memcmp(b1 As Byte(), b2 As Byte(), count As UIntPtr) _ As Integer End Function Public Shared Function ByteArrayCompare(a As Byte(), b As Byte()) As Boolean If Object.ReferenceEquals(a, b) Then Return True End If If a Is Nothing OrElse b Is Nothing OrElse a.Length <> b.Length Then Return False End If Return memcmp(a, b, New UIntPtr(CUInt(a.Length))) = 0 End Function
[System.Runtime.InteropServices.DllImport("msvcrt.dll", CallingConvention = System.Runtime.InteropServices.CallingConvention.Cdecl)] private static extern int memcmp(byte[] b1, byte[] b2, UIntPtr count); public static bool ByteArrayCompare(byte[] a, byte[] b) { if (object.ReferenceEquals(a, b)) { return true; } if (a == null || b == null || a.Length != b.Length) { return false; } return memcmp(a, b, new UIntPtr((uint)a.Length)) == 0; }
「Uhuru Mkate: Byte Array Comparison Benchmarks」などによると、IStructuralEquatableを使った方法はかなり遅く、SequenceEqualもFor文で単純に比較する方法より遅いようです。バイト型配列であれば、アンセーフコードとmemcmpの方法は共に同じ位高速のようです。