重複のないデータを扱う - C#でユニーク値を扱う - HashSetクラスの利用 - C#

HashSetクラスを使ってキーのみ(値のみ)のデータを扱う方法を紹介します。

概要

C#でユニークなキーを管理する際にはHashSetクラスを利用できます。この記事ではHashSetクラスを利用してデータの格納や取り出しをするコードを紹介します。

HashSetへの値の追加と重複のない列挙

UI

下図のUIを作成します。Buttonを一つMultilineプロパティをtrueにしたTextBoxをフォームに配置します。

コード

下記のコードを記述します。
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を利用して記述します。

実行結果

プロジェクトを実行します。下図のウィンドウが表示されます。


[Button1]をクリックします。テキストボックスに下図の結果が表示されます。重複要素のAdd時にはfalseが返されることがわかります。また、 Addメソッドで重複する要素を追加しましたが、HashSetには重複なく要素が格納されることが確認できました。

HashSetに要素を含んでいるか

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

UI

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

コード

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

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";
  }
}

実行結果

プロジェクトを実行します。下図のウィンドウが表示されます。


テキストボックスに"ペンギン"や"にわとり"を入力してボタンをクリックした場合は、下部のテキストボックスに "要素に含まれています。" のメッセージが表示されます。



HashSetに追加していない値をテキストボックスに入力してボタンをクリックした場合は、下部のテキストボックスに "要素に含まれていません。" のメッセージが表示されます。

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

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);
      }

    }
  }
}

実行結果

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

著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用
掲載日: 2015-11-29
iPentec all rights reserverd.