Genericsで値の重複をさせない動作にする / Listで値を重複させない - HashSet の利用

List<T>などのジェネリック型で値の重複させない動作にするコードと実行結果を紹介します。

概要

List<T>を利用すると、複数の値をリスト形式で管理できます。 List<T>の場合は値の検証がされないため、リスト内に複数の同じ値を持つ要素が設定される場合があります。 利用用途によっては、同じ値の重複を避けたい場合があります。

C#でユニークなキーを管理する際にはHashSetクラスを利用できます。 HashSetを利用すると、同じ値の重複をしない動作が実装できます。この記事ではHashSetを利用したコードと実行結果を紹介します。

プログラム例: HashSetの利用1

UI

下図のフォームを作成します。
ボタンを2つ、MultilineプロパティをTrueに設定したテキストをボックスを配置します。
Genericsで値の重複をさせない動作にする / Listで値を重複させない - HashSet の利用:画像1

コード

以下のコードを記述します。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace HashSetDemo
{
  public partial class FormSimpleHashSet : Form
  {
    public FormSimpleHashSet()
    {
      InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
      HashSet<string> hs = new HashSet<string>();
      hs.Add("ぺんぎんクッキー");
      hs.Add("らくだキャラメル");
      hs.Add("あひるタルト");

      List<string> list = hs.ToList();
      for (int i = 0; i < list.Count; i++) {
        textBox1.Text += list[i] + "\r\n";
      }
    }

    private void button2_Click(object sender, EventArgs e)
    {
      HashSet<string> hs = new HashSet<string>();
      hs.Add("ぺんぎんクッキー");
      hs.Add("らくだキャラメル");
      hs.Add("ぺんぎんクッキー");
      hs.Add("あひるタルト");
      hs.Add("あひるタルト");
      hs.Add("しろくまアイス");
      hs.Add("らくだキャラメル");

      List<string> list = hs.ToList();
      for (int i = 0; i < list.Count; i++) {
        textBox1.Text += list[i] + "\r\n";
      }
    }
  }
}

解説

[button1]は値に重複の無い要素をHashSetに追加します。
追加後に、HashSetの内容をテキストボックスに表示します。HashSetクラスのToList()メソッドを呼び出すことで、List<T>に変換できます。

    private void button1_Click(object sender, EventArgs e)
    {
      HashSet<string> hs = new HashSet<string>();
      hs.Add("ぺんぎんクッキー");
      hs.Add("らくだキャラメル");
      hs.Add("あひるタルト");

      List<string> list = hs.ToList();
      for (int i = 0; i < list.Count; i++) {
        textBox1.Text += list[i] + "\r\n";
      }
    }


[button2]も[button1]と同様のコードでが、重複する値を追加しています。

    private void button2_Click(object sender, EventArgs e)
    {
      HashSet<string> hs = new HashSet<string>();
      hs.Add("ぺんぎんクッキー");
      hs.Add("らくだキャラメル");
      hs.Add("ぺんぎんクッキー");
      hs.Add("あひるタルト");
      hs.Add("あひるタルト");
      hs.Add("しろくまアイス");
      hs.Add("らくだキャラメル");

      List<string> list = hs.ToList();
      for (int i = 0; i < list.Count; i++) {
        textBox1.Text += list[i] + "\r\n";
      }
    }

実行結果

プロジェクトを実行します。下図のウィンドウが表示されます。
Genericsで値の重複をさせない動作にする / Listで値を重複させない - HashSet の利用:画像2

[button1]をクリックします。重複する値を追加していないため、HashSetに追加した値がテキストボックスに表示されます。
Genericsで値の重複をさせない動作にする / Listで値を重複させない - HashSet の利用:画像3

[button2]をクリックします。重複する値を追加していますが、重複する値はHashSetに追加されず、重複しない値が1回ずつ表示されます。
Genericsで値の重複をさせない動作にする / Listで値を重複させない - HashSet の利用:画像4

プログラム例: HashSetの利用2

UI

下図のUIを作成します。Buttonを一つMultilineプロパティをtrueにしたTextBoxをフォームに配置します。
Genericsで値の重複をさせない動作にする / Listで値を重複させない - HashSet の利用:画像5

コード

下記のコードを記述します。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace HashSetDemo
{
  public partial class FormMain : Form
  {
    public FormMain()
    {
      InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
      HashSet<string> hashSet = new HashSet<string>();
      bool ret;

      ret = hashSet.Add("ペンギン");
      textBox1.Text += Convert.ToString(ret)+"\r\n";
      ret = hashSet.Add("ペンギン");
      textBox1.Text += Convert.ToString(ret) + "\r\n";
      ret = hashSet.Add("くじら");
      textBox1.Text += Convert.ToString(ret) + "\r\n";
      ret = hashSet.Add("にわとり");
      textBox1.Text += Convert.ToString(ret) + "\r\n";
      ret = hashSet.Add("くじら");
      textBox1.Text += Convert.ToString(ret) + "\r\n";
      ret = hashSet.Add("ハト");
      textBox1.Text += Convert.ToString(ret) + "\r\n";
      ret = hashSet.Add("くじら");
      textBox1.Text += Convert.ToString(ret) + "\r\n";
      ret = hashSet.Add("ペンギン");
      textBox1.Text += Convert.ToString(ret) + "\r\n";
      ret = hashSet.Add("にわとり");
      textBox1.Text += Convert.ToString(ret) + "\r\n";
      ret = hashSet.Add("ハト");
      textBox1.Text += Convert.ToString(ret) + "\r\n";
      ret = hashSet.Add("くじら");
      textBox1.Text += Convert.ToString(ret) + "\r\n";

     textBox1.Text += "-------\r\n";

      foreach (string s in hashSet) {
        textBox1.Text += s + "\r\n";
      }
    }
  }
}

解説

今回のプログラムはButtonのClickイベントのみの実装となるため、ボタンがクリックされた際にコードが実行されます。

  HashSet<string> hashSet = new HashSet<string>();
により、HashSetクラスのインスタンスを作成します。HashSetは HashSet<T> で定義されており、キーにしたい型を指定します。今回の例ではstring型を指定しています。

  ret = hashSet.Add("ペンギン");
  textBox1.Text += Convert.ToString(ret)+"\r\n";
上記のコードのHashSetクラスのインスタンスであるhashSetオブジェクトのAddメソッドを呼び出して、要素を追加します。Addメソッドの戻り値は、Addメソッドの引数に与えた要素が重複していない新規の要素である場合にはtrueを返します。すでにHashSetオブジェクトに追加されている要素であった場合は、falseを返します。2行目は戻り値をTextBoxに表示するコードです。

  foreach (string s in hashSet) {
    textBox1.Text += s + "\r\n";
  }

HashSetオブジェクトに登録されている要素を列挙してtextBox1に表示します。要素の列挙はforeachを利用して記述します。

実行結果

プロジェクトを実行します。下図のウィンドウが表示されます。
Genericsで値の重複をさせない動作にする / Listで値を重複させない - HashSet の利用:画像6

[Button1]をクリックします。テキストボックスに下図の結果が表示されます。重複要素のAdd時にはfalseが返されることがわかります。また、 Addメソッドで重複する要素を追加しましたが、HashSetには重複なく要素が格納されることが確認できました。
Genericsで値の重複をさせない動作にする / Listで値を重複させない - HashSet の利用:画像7

プログラム例: HashSetに要素を含んでいるかの確認

HashSetに要素が含まれているかを確認するコードを紹介します。

UI

下図のUIを作成します。TextBoxを2つ、Buttonを1つ配置します。TextBoxの一つは MultilineプロパティをTrueに設定し複数行テキストボックスとします。

コード

下記のコードを記述します。
Genericsで値の重複をさせない動作にする / Listで値を重複させない - HashSet の利用:画像8

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace HashSetContainsDemo
{
  public partial class FormMain : Form
  {
    private HashSet<string> hashSet = new HashSet<string>();

    public FormMain()
    {
      InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
      if (hashSet.Contains(textBox2.Text) == true) {
        textBox1.Text += "要素に含まれています。\r\n";
      }
      else {
        textBox1.Text += "要素に含まれていません。\r\n";
      }
    }

    private void FormMain_Load(object sender, EventArgs e)
    {
      hashSet.Add("ペンギン");
      hashSet.Add("ペンギン");
      hashSet.Add("くじら");
      hashSet.Add("にわとり");
      hashSet.Add("くじら");
      hashSet.Add("ハト");
      hashSet.Add("くじら");
      hashSet.Add("ペンギン");
      hashSet.Add("にわとり");
      hashSet.Add("ハト");
      hashSet.Add("くじら");
    }
  }
}

解説

フォームのロード時に下記のコードによりHashSetオブジェクトに値を追加します。

private void FormMain_Load(object sender, EventArgs e)
{
  hashSet.Add("ペンギン");
  hashSet.Add("ペンギン");
  hashSet.Add("くじら");
  hashSet.Add("にわとり");
  hashSet.Add("くじら");
  hashSet.Add("ハト");
  hashSet.Add("くじら");
  hashSet.Add("ペンギン");
  hashSet.Add("にわとり");
  hashSet.Add("ハト");
  hashSet.Add("くじら");
}


ボタンクリック時には下記のコードが実行されます。HashSetクラスのContains()メソッドを呼び出すことで、HashSetに値が存在しているかを確認できます。Containsメソッドの第一引数に、確認するHashSetの値を与えます。
下記コードの例では、TextBox2に入力された文字列が、HashSetに存在しているかをContains()メソッドを呼び出して確認します。HashSetに要素が含まれていれば、Contains()メソッドの戻り値はtrueとなり、テキストボックスに "要素に含まれています。" のメッセージを表示します。HashSetに要素が含まれていない場合は、Contains()メソッドの戻り値はfalseとなるため、テキストボックスに "要素に含まれていません。" のメッセージを表示します。

private void button1_Click(object sender, EventArgs e)
{
  if (hashSet.Contains(textBox2.Text) == true) {
    textBox1.Text += "要素に含まれています。\r\n";
  }
  else {
    textBox1.Text += "要素に含まれていません。\r\n";
  }
}

実行結果

プロジェクトを実行します。下図のウィンドウが表示されます。
Genericsで値の重複をさせない動作にする / Listで値を重複させない - HashSet の利用:画像9

テキストボックスに"ペンギン"や"にわとり"を入力してボタンをクリックした場合は、下部のテキストボックスに "要素に含まれています。" のメッセージが表示されます。
Genericsで値の重複をさせない動作にする / Listで値を重複させない - HashSet の利用:画像10 Genericsで値の重複をさせない動作にする / Listで値を重複させない - HashSet の利用:画像11

HashSetに追加していない値をテキストボックスに入力してボタンをクリックした場合は、下部のテキストボックスに "要素に含まれていません。" のメッセージが表示されます。
Genericsで値の重複をさせない動作にする / Listで値を重複させない - HashSet の利用:画像12 Genericsで値の重複をさせない動作にする / Listで値を重複させない - HashSet の利用:画像13

補足:クラスを要素に指定した場合

HashSetの要素にクラスを指定した場合の動作を紹介します。
下記のコードを記述します。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace HashSetClassItem
{
  public partial class FormMain : Form
  {
    public class MyItem
    {
      public int Value;
      public string Name;
    }

    public FormMain()
    {
      InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
      HashSet<MyItem> hashSet = new HashSet<MyItem>();
      MyItem mi;
      
      mi = new MyItem();
      mi.Value = 120;
      mi.Name = "ぺんぎんクッキー";
      hashSet.Add(mi);

      mi = new MyItem();
      mi.Value = 330;
      mi.Name = "しろくまケーキ";
      hashSet.Add(mi);

      mi = new MyItem();
      mi.Value = 120;
      mi.Name = "ぺんぎんクッキー";
      hashSet.Add(mi);

      mi = new MyItem();
      mi.Value = 240;
      mi.Name = "ぺんぎんクッキー";
      hashSet.Add(mi);

      mi = new MyItem();
      mi.Value = 330;
      mi.Name = "しろくまケーキ";
      hashSet.Add(mi);

      mi = new MyItem();
      mi.Value = 90;
      mi.Name = "あひるキャンディー";
      hashSet.Add(mi);
      
      foreach (MyItem m in hashSet) {
        textBox1.Text += string.Format("{0}, {1:d}\r\n", m.Name, m.Value);
      }

    }
  }
}

実行結果

プロジェクトを実行し、ボタンをクリックすると下図の結果が表示されます。値が同じである要素が複数表示されています。クラスの場合はオブジェクトのアドレスが一致しているものが同じ要素と判定されるため、クラス内のメンバ変数が同じであっても、同一の要素とは判定されません。
Genericsで値の重複をさせない動作にする / Listで値を重複させない - HashSet の利用:画像14

AuthorPortraitAlt
著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用
作成日: 2015-11-29
改訂日: 2025-02-08
Copyright © 1995–2025 iPentec all rights reserverd.