Listのソート/カスタム比較

Listのソート (Comparison を用いたカスタムソート) - C#

Comparisonを用いたリストのカスタムソートを紹介します。

概要

クラスオブジェクトのListを作成したときなど、リストのソート時にクラスオブジェクトの特定のメンバ変数の値順にソートしたいことがあります。
int型やstring型のリストでは標準のソートメソッドの呼び出しで値順にソートできますが、クラスオブジェクトの場合はどのメンバ変数を利用するかなど、 実装者がロジックを記述する必要があります。
この記事では、Comparisonを実装することで、クラスオブジェクトのリストをカスタムソートするコードを紹介します。

補足

この記事で紹介する方法は与えられた2つのクラスオブジェクトのメンバ変数の値などを比較して、どちらのオブジェクトが大きいかを判定する Comparisonメソッドを実装するコードです。
そのため、外部の状態やこれまでの比較結果の状態を保存することは基本的にできません。外部の状態やこれまでの比較結果が判定結果に影響する、 さらに複雑なロジックを実装する場合は、Comparisonではなく、IComparer インターフェイスを実装した比較用のクラスを実装する方法を用います。 IComparer インターフェイスを利用したListのソートについてはこちらの記事を参照してください。

プログラム例

UI

下図のUIを準備します。使用するのはボタン1つとテキストボックスです。

コード

以下のコードを記述します。
FormMain.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

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

    private void button2_Click(object sender, EventArgs e)
    {
      List<Info> InfoList = new List<Info>();
      Info inf;

      inf = new Info();
      inf.Name="Penguin";
      inf.value=100;
      InfoList.Add(inf);

      inf = new Info();
      inf.Name = "Duck";
      inf.value = 30;
      InfoList.Add(inf);

      inf = new Info();
      inf.Name = "Pigion";
      inf.value = 50;
      InfoList.Add(inf);

      inf = new Info();
      inf.Name = "Swallow";
      inf.value = 200;
      InfoList.Add(inf);

      inf = new Info();
      inf.Name = "Spallow";
      inf.value = 50;
      InfoList.Add(inf);

      inf = new Info();
      inf.Name = "Hawk";
      inf.value = 50;
      InfoList.Add(inf);

      InfoList.Sort(Compare);

      for (int i = 0; i < InfoList.Count; i++ ) {
        textBox_Output.Text += string.Format("{0:s}:{1:d}\r\n",
          InfoList[i].Name, InfoList[i].value);
      }
    }

    private static int Compare(Info x, Info y)
    {
      if (x.value < y.value) {
        return -1;  
      }
      else if (x.value > y.value) {
        return 1;
      }
      else {
        if (x.Name[0] < y.Name[0]) {
          return -1;
        }
        else if (x.Name[0] < y.Name[0]){
          return 1;
        }else{
           return 0;
        }
      }
    }

  }
}

解説

List<Info> InfoList = new List<Info>();
InfoクラスのListを宣言してインスタンスを作成します。

inf = new Info();
inf.Name="Penguin";
inf.value=100;
InfoList.Add(inf);
...
にて、InfoListにInfoクラスの要素を追加します。

InfoList.Sort(Compare);
リストをソートします。引数のComparisonに比較メソッドを与えます。

for (int i = 0; i < InfoList.Count; i++ ) {
  textBox_Output.Text += string.Format("{0:s}:{1:d}\r\n",
  InfoList[i].Name, InfoList[i].value);
}
InfoListの要素を先頭から順番にTextBoxに表示します。

private static int Compare(Info x, Info y)
{
  if (x.value < y.value) {
    return -1;  
  }
  else if (x.value > y.value) {
    return 1;
  }
  else {
    if (x.Name[0] < y.Name[0]) {
      return -1;
    }
    else if (x.Name[0] < y.Name[0]){
      return 1;
    }else{
       return 0;
    }
  }
}
上記が要素の比較用のメソッドです。Infoクラスのvalueの値を比較し昇順にソートします。valueクラスの値が同じ場合はNameの1文字目を比較し昇順にソートします。

info.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ListSort
{
  public class Info
  {
    public string Name;
    public int value;
  }
}

解説

infoクラスの実行コードです。Nameとvalueのメンバ変数を定義しています。

実行結果

アプリケーションを起動すると下図の画面が表示されます。

コードを実装したボタン、(今回は[Exec2]ボタン)をクリックすると、下図の結果が表示されます。valueの値でソートされていること、valueの値が同じ場合はNameの昇順でソートされていることが確認できました。


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