ここでは、例えば{ 1, 2, 3 }と{ 1, 4, 3 }という2つの配列から{ 1, 3 }という配列を作るというように、2つの配列(またはコレクション)の両方に含まれている要素のみを抜き出す方法を紹介します。つまり、積集合、共通部分を取得する方法ということになります。
まずは最も基本的な、For文を使った方法から見てみましょう。以下の例では、For文を使って片方の配列の要素を列挙し、その要素がもう片方の配列に含まれているか調べ、もし含まれていれば新しく作成したコレクションに追加しています。
以下にその例を示します。
'比較する2つの配列 Dim ary1 As Integer() = New Integer() {1, 2, 3, 3} Dim ary2 As Integer() = New Integer() {1, 3, 4, 3} 'どちらの配列にも含まれる要素を覚えておくためのコレクション '.NET Framework 2.0以降ならば、List(Of Integer)を使った方が良い Dim newList As New System.Collections.ArrayList(ary1.Length + ary2.Length) 'ary1の要素を列挙する Dim i As Integer For Each i In ary1 'ary2に含まれていて、newListにまだ含まれていない要素を探す If 0 <= Array.IndexOf(ary2, i) AndAlso Not newList.Contains(i) Then 'newListに追加する newList.Add(i) End If Next '結果を配列に変換する Dim newArray As Integer() = _ DirectCast(newList.ToArray(GetType(Integer)), Integer()) 'newArrayは{ 1, 3 }となる
//比較する2つの配列 int[] ary1 = new int[] { 1, 2, 3, 3 }; int[] ary2 = new int[] { 1, 3, 4, 3 }; //どちらの配列にも含まれる要素を覚えておくためのコレクション //.NET Framework 2.0以降ならば、List<int>を使った方が良い System.Collections.ArrayList newList = new System.Collections.ArrayList(ary1.Length + ary2.Length); //ary1の要素を列挙する foreach (int i in ary1) { //ary2に含まれていて、newListにまだ含まれていない要素を探す if (0 <= Array.IndexOf(ary2, i) && !newList.Contains(i)) { //newListに追加する newList.Add(i); } } //結果を配列に変換する int[] newArray = (int[])newList.ToArray(typeof(int)); //newArrayは{ 1, 3 }となる
.NET Framework 2.0以降であれば、Array.FindAllメソッドやList(T).FindAllメソッドを使って抽出する方法があります。FindAllメソッドについて詳しくは、「配列やコレクションのフィルタ処理を行う(条件に合う要素を抜き出す)」で説明しています。
以下に、2つの配列とListで共通の要素を探す簡単な例を示します。この例では、抽出する条件を片方の配列のContainsメソッドがTrueを返す時としていますので、抽出結果の配列には同じ要素が複数含まれる可能性があります。
'Imports System.Collections.Generic '比較する2つの配列 Dim ary1 As Integer() = New Integer() {1, 2, 3, 3} Dim ary2 As Integer() = New Integer() {1, 3, 4, 3} 'ary1の要素の内、ary2.ContainsがTrueとなる要素を取得する Dim newArray As Integer() = _ Array.FindAll(ary1, AddressOf DirectCast(ary2, IList(Of Integer)).Contains) 'newListは{ 1, 3, 3 }となる '比較する2つのList Dim list1 As New List(Of Integer)(ary1) Dim list2 As New List(Of Integer)(ary2) 'list1の要素の内、list2.ContainsがTrueとなる要素を取得する Dim newList As List(Of Integer) = list1.FindAll(AddressOf list2.Contains) 'newListは{ 1, 3, 3 }となる
//using System.Collections.Generic; //比較する2つの配列 int[] ary1 = new int[] { 1, 2, 3, 3 }; int[] ary2 = new int[] { 1, 3, 4, 3 }; //ary1の要素の内、ary2.ContainsがTrueとなる要素を取得する int[] newArray = Array.FindAll(ary1, ((IList<int>)ary2).Contains); //newListは{ 1, 3, 3 }となる //比較する2つのList List<int> list1 = new List<int>(ary1); List<int> list2 = new List<int>(ary2); //list1の要素の内、list2.ContainsがTrueとなる要素を取得する List<int> newList = list1.FindAll(list2.Contains); //newListは{ 1, 3, 3 }となる
「c# - Removal operation in List<string> - Stack Overflow」によると、List.ContainsメソッドがO(n)操作なのに対してHashSet.ContainsメソッドはO(1)操作のため、こちらを使用した方がパフォーマンスが良いということです。HashSetクラスは、.NET Framework 3.5以降で使用できます。
'Imports System.Collections.Generic '比較する2つの配列 Dim ary1 As Integer() = New Integer() {1, 2, 3, 3} Dim ary2 As Integer() = New Integer() {1, 3, 4, 3} 'ary2をHashSetに変換する Dim hs As New HashSet(Of Integer)(ary2) 'ary1の要素の内、hs.ContainsがTrueとなる要素を取得する Dim newArray As Integer() = Array.FindAll(ary1, AddressOf hs.Contains)
//using System.Collections.Generic; //比較する2つの配列 int[] ary1 = new int[] { 1, 2, 3, 3 }; int[] ary2 = new int[] { 1, 3, 4, 3 }; //ary2をHashSetに変換する HashSet<int> hs = new HashSet<int>(ary2); //ary1の要素の内、hs.ContainsがTrueとなる要素を取得する int[] newArray = Array.FindAll(ary1, hs.Contains);
.NET Framework 3.5以降でLinqを使えるのであれば、Enumerable.Intersectメソッドを使って積集合のシーケンスを作成できます。
'Imports System.Linq '比較する2つの配列(コレクションでも可) Dim ary1 As Integer() = New Integer() {1, 2, 3, 3} Dim ary2 As Integer() = New Integer() {1, 3, 4, 3} '2つの配列の積集合を作成し、配列に変換する Dim newArray As Integer() = ary1.Intersect(ary2).ToArray() 'newArrayは{ 1, 3 }となる
//using System.Linq; //比較する2つの配列(コレクションでも可) int[] ary1 = new int[] { 1, 2, 3, 3 }; int[] ary2 = new int[] { 1, 3, 4, 3 }; //2つの配列の積集合を作成し、配列に変換する int[] newArray = ary1.Intersect(ary2).ToArray(); //newArrayは{ 1, 3 }となる
.NET Framework 3.5以降では、HashSet.IntersectWithメソッドを使うこともできます。このメソッドは比較する両方のコレクションがHashSetの場合、O(n)操作になりますので、パフォーマンスがかなり良いです。HashSet以外との比較でもO(n + m)操作ですので、通常よりはパフォーマンスが良いです。
'Imports System.Collections.Generic '比較する2つのHashSet Dim hs1 As New HashSet(Of Integer)() From {1, 2, 3, 3} Dim hs2 As New HashSet(Of Integer)() From {1, 3, 4, 3} '両方のHashSetに含まれている要素だけにする hs1.IntersectWith(hs2) '配列に変換する Dim newArray As Integer() = New Integer(hs1.Count - 1) {} hs1.CopyTo(newArray)
//using System.Collections.Generic; //比較する2つのHashSet HashSet<int> hs1 = new HashSet<int> { 1, 2, 3, 3 }; HashSet<int> hs2 = new HashSet<int> { 1, 3, 4, 3 }; //両方のHashSetに含まれている要素だけにする hs1.IntersectWith(hs2); //配列に変換する int[] newArray = new int[hs1.Count]; hs1.CopyTo(newArray);