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