ITコンサルの日常

ITコンサル会社に勤務する普通のITエンジニアの日常です。

IComparableとIComparer(オブジェクトのソートにまつわる話)

プログラミングC# p218

IComparableとIComparerを使ってオブジェクトの比較方法を定義し、ソート順を決められます。
再びRectangleクラス。xの持つ値順に並び替えられるようにしてみます。

using System;
using System.Collections;
using System.Collections.Generic;

class Rectangle : IComparable<Rectangle>
{
        private int x;
        private int y;

        public Rectangle(int x, int y)
        {
                this.x = x;
                this.y = y;
        }

        public int CompareTo(Rectangle r)
        {
                if(this.x == r.x)
                        return 0;

                return this.x > r.x ? this.x : r.x;
        }

        public override string ToString()
        {
                return "(" + this.x + "," + this.y + ")";
        }

        static void Main(string[] args)
        {
                Rectangle[] rectArray = new Rectangle[2];
                rectArray[0] = new Rectangle(5, 4);
                rectArray[1] = new Rectangle(4, 5);

                Array.Sort(rectArray);
                foreach(Rectangle r in rectArray)
                {
                        Console.WriteLine(r);
                }
        }
}

結果はこう。

(4,5)
(5,4)

配列を作成したときは、(5,4), (4,5)の順でしたが、並び替えた結果はxの昇順になって(4,5), (5,4)となりました。
ここまではわりかし簡単。yで並び替えたかったり、x→yの順で並び替えたかったりしたら、compareToメソッドの中身を適当にいじれば出来ます。


じゃあ、場合によってxで並び替えたかったり、yで並び替えたかったりした場合はどうするか。
こういう場合は、IComparerを使います。

class Rectangle
{
        private int x;
        private int y;

        public Rectangle(int x, int y)
        {
                this.x = x;
                this.y = y;
        }

        public class OrderByX : IComparer<Rectangle>
        {
                public int Compare(Rectangle r1, Rectangle r2)
                {
                        if(r1.x == r2.x)
                                return 0;

                        return r1.x > r2.x ? r1.x : r2.x;
                }
        }

        public class OrderByY : IComparer<Rectangle>
        {
                public int Compare(Rectangle r1, Rectangle r2)
                {
                        if(r1.y == r2.y)
                                return 0;

                        return r1.y > r2.y ? r1.y : r2.y;
                }
        }

        public override string ToString()
        {
                return "(" + this.x + "," + this.y + ")";
        }

        static void Main(string[] args)
        {
                Rectangle[] rectArray = new Rectangle[2];
                rectArray[0] = new Rectangle(4, 5);
                rectArray[1] = new Rectangle(5, 4);

                Array.Sort(rectArray, new Rectangle.OrderByX());
                foreach(Rectangle r in rectArray)
                {
                        Console.WriteLine(r);
                }

                Array.Sort(rectArray, new Rectangle.OrderByY());
                foreach(Rectangle r in rectArray)
                {
                        Console.WriteLine(r);
                }
        }
}

結果はこう。

(5,4)
(4,5)
(4,5)
(5,4)

System.Array.Sortの第二引数にIComparerを渡すわけですが、ここをxで並び替えたい場合はx用のIComparerを、yで並び替えたい場合はy用のIComparerを渡すようにします。


というわけでまとめ。

  • クラスのソート方法が決まっている場合は、そのクラスでIComparableを実装する。
  • クラスのソート方法が二つ以上あり切り替えたい場合は、IComparerの実装を用意する。

というところでしょう。


ちなみにJavaでは、
IComparable → java.lang.Comparable
IComparer → java.util.Comparator
です。


Comparableの例

import java.util.*;

public class IComparableTest implements Comparable<IComparableTest>
{
        private int x;
        private int y;

        public IComparableTest(int x, int y)
        {
                this.x = x;
                this.y = y;
        }

        public int compareTo(IComparableTest r)
        {
                if(this.x == r.x)
                        return 0;

                return this.x > r.x ? this.x : r.x;
        }

        public String toString()
        {
                return "(" + this.x + "," + this.y + ")";
        }

        public static void main(String[] args)
        {
                IComparableTest[] rectArray = new IComparableTest[2];
                rectArray[0] = new IComparableTest(5, 4);
                rectArray[1] = new IComparableTest(4, 5);

                Arrays.sort(rectArray);
                for(IComparableTest r : rectArray)
                {
                        System.out.println(r);
                }
        }
}
|<

Comparatorの例
>|java|
import java.util.*;

class ComparatorTest
{
        private int x;
        private int y;

        public ComparatorTest(int x, int y)
        {
                this.x = x;
                this.y = y;
        }

        public static class OrderByX implements Comparator<ComparatorTest>
        {
                public int compare(ComparatorTest r1, ComparatorTest r2)
                {
                        if(r1.x == r2.x)
                                return 0;

                        return r1.x > r2.x ? r1.x : r2.x;
                }
        }

        public static class OrderByY implements Comparator<ComparatorTest>
        {
                public int compare(ComparatorTest r1, ComparatorTest r2)
                {
                        if(r1.y == r2.y)
                                return 0;

                        return r1.y > r2.y ? r1.y : r2.y;
                }
        }

        public String toString()
        {
                return "(" + this.x + "," + this.y + ")";
        }

        public static void main(String[] args)
        {
                ComparatorTest[] rectArray = new ComparatorTest[2];
                rectArray[0] = new ComparatorTest(4, 5);
                rectArray[1] = new ComparatorTest(5, 4);

                Arrays.sort(rectArray, new ComparatorTest.OrderByX());
                for(ComparatorTest r : rectArray)
                {
                        System.out.println(r);
                }

                Arrays.sort(rectArray, new ComparatorTest.OrderByY());
                for(ComparatorTest r : rectArray)
                {
                        System.out.println(r);
                }
        }
}