右下のようなクラスがあり、それを使った実験用の配列を生成し、その配列を取得するため、 以下のクラスがあるとします。
package rec; import rec.Record2; import rec.Record3; public class Rec23Data{ //実験用の配列を生成し、その配列を戻り値とするメソッド public static Record2[] getData(){ Record2[] a = new Record2[]{ new Record2("A01", 1), new Record3("A02", 2, 200), new Record2("A03", 3), new Record2("A04", 4), new Record3("B01", 10,1000), new Record3("B02", 20,2000), new Record2("B02", 30), new Record3("B03", 30,300), }; return a; } // iStart から(iEnd-1)の添え字情報を表示 public static void display(Record2[] a,int iStart,int iEnd){ for (int i = iStart; i < iEnd; i++){ a[i].display(i); } } public static void main(String[] arg){ Record2[] a = Rec23Data.getData(); Rec23Data.display(a, 0, 8);//確認用表示 } } |
ページ先頭のRec23Data.getData()で得られる配列から、数量が10のレコードを探す2分探索のプログラムを検討します。
これまでは、標準で使うであろうと考えられる商品コードで探しましたが、それと違うフィールドで検索するわけです。
そして、前回はjava,lang.Comparableインターフェイスを利用しましたが、
今度は、通常と違う比較や等価用のComparatorインターフェイスを利用します。
これは、
java.utilパッケージで次のように用意されています。(なお、以下はJDK.1.4以前の古い形式です)
package java.util; public interface Comparator { int compare(Object o1, Object o2);// o1とo2の大小比較 boolean equals(Object obj);//等価チェック }
既存
の上記Comparatorインターフェイスを利用して、まず汎用的に使えるメソッドを
anyパッケージに、ObjArrayクラスのstaticメソッドとして作る例を以下に示します。
引数の最後のcmpで、比較方法のメソッドを持つオブジェクトを与えて、その比較で2分探索を
行うイメージです。
package any; import java.util.Comparator; public class ObjArray { public static int binSearch(Object[] a, Object key, int iStart, int iEnd, Comparator cmp){ int iL = iStart; //現在の探索範囲で、先頭の添え字(左) int iR = iEnd - 1; //現在の探索範囲で、末尾の添え字(右) while (iL <= iR){ int iC = (iL + iR) / 2; //現在の探索範囲で、中央の添え字(中心) int val = cmp.compare(a[iC], key);//比較 if (val == 0){ return iC;//見つかった } else if (val < 0){//keyが大きい iL = iC + 1; } else {/* keyが小さい */ iR = iC - 1; } } return -1; //見つからない } }
Comparator cmpのインターフェイスの引数を使い、
cmp.compare(a[iC], key)で、2つの引数を比較しています。
検索対象が、Object[] a, Object keyとObjectクラスなので、これから作る任意クラスのオブジェクトでも指定できます。
よって、Comparatorインターフェイスをimplementsするクラスは、Record2でないくてもよく、
任意のクラスで作れます。以下では新しく、RecSuuCmp.javaのファイルで作成した例です。
Record2のsuuで数量を比較するだけのクラスです。
Comparatorインターフェイスでは、equalsメソッドの宣言もあるので、
それも実装するところですが、Objectクラスで、既に存在するメソッドなので、
作らなくてもエラーになりません。
(必要なら、compareの戻り値が0になるような比較をequalsで行った時、戻り値がtrueになるような
同じ種類の等価が調べられよう作るべきでしょう。)
package rec; import java.util.Comparator; public class RecSuuCmp implements Comparator{ // o1とo2の大小比較を行うComparatorインターフェイス実装メソッド public int compare(Object o1, Object o2){ Record2 r1 = (Record2)o1; Record2 r2 = (Record2)o2; return r1.suu - r2.suu; } //等価チェックするComparatorインターフェイス実装メソッド // 必要なら、以下のコメントを外し、オーバーライドします。 // Objectクラスのequalsと同じチェックでよい場合は、省略します。 //public boolean equals(Object obj) //{ // return this.equals(obj);//Objectクラスのequals呼び出し //} }
以下でこれを利用して、数量が10のレコードを探すプログラムを示します。
import rec.Record2; import rec.RecSuuCmp; public class Test { public static void main(String[] arg) { Record2[] a = rec.Rec23Data.getData(); Object key = new Record2("", 10);// 探したいデータ int iFound = any.ObjArray.binSearch(a, key, 0, 8, new RecSuuCmp()); //実際に存在するデータ範囲のを指定 if (iFound == -1){ System.out.println("見つかりません"); System.exit(0);//実行終了 } a[iFound].display(iFound); } }
これは、binSearchメソッドの最後の引数RecSuuCmpオブジェクトで比較し、探索させています。
以下に実行例をし示します。
D:\java>java Test 4番目レコード 商品コード:B01 数量:10 単価:1000 D:\java>
なお、このプログラム JDK1.5 以上でコンパイルすると、次のような警告がでます。
D:\java>javac Test.java 注: .\any\ObjArray.java の操作は、未チェックまたは安全ではありません。 注: 詳細については、-Xlint:unchecked オプションを指定して再コンパイルしてください。 D:\java>javac -Xlint:unchecked Test.java .\any\ObjArray.java:13: 警告: [unchecked] raw 型 java.util.Comparator のメンバと しての compare(T,T) への無検査呼び出しです。 int val = cmp.compare(a[iC], key);//比較 ^ 警告 1 個 D:\java>
警告は出ますが、問題なくコンパイルできます。
実は、上記Comparatorインターフェイスの使い方が、JDK1.4 以下の作り方で、
JDK1.5以上では、避けるべき作り方であるためです。詳細は後述しますが、このような警告を避ける場合は、
次の指定でコンパイルください。
D:\java>javac -source 1.4 Test.java D:\java>javac Test.java
-source 1.4の指定で、ソースの書き方が1.4であることを指定しています。
参考にComparatorインターフェイスのequalsメソッドを追加して実装した例も示します。
package rec; import java.util.Comparator; public class RecSuuCmp implements Comparator { int value;//等価チェックするデータ public RecSuuCmp() { }//コンストラクタ public RecSuuCmp(int key){//コンストラクタ this.value = key; } // o1とo2の大小比較を行うComparatorインターフェイス実装メソッド public int compare(Object o1, Object o2){ Record2 r1 = (Record2)o1; Record2 r2 = (Record2)o2; return r1.suu - r2.suu; } //等価チェックするComparatorインターフェイス実装メソッド public boolean equals(Object obj){ Record2 iObj = (Record2)obj; return this.value == iObj.suu; } }
2分探索(binSearch)に変更はありません。 この追加したequalsメソッドは、次のようにTest.javaで 使ってみます。
import rec.Record2;
import rec.RecSuuCmp;
public class Test
{
public static void main(String[] arg) {
Record2[] a = rec.Rec23Data.getData();
Object key = new Record2("", 10);// 探したいデータ
int iFound = any.ObjArray.binSearch(a, key, 0, 8, new RecSuuCmp());
//実際に存在するデータ範囲のを指定
if (iFound == -1){
System.out.println("見つかりません");
System.exit(0);//実行終了
}
a[iFound].display(iFound);
RecSuuCmp suuCmp = new RecSuuCmp(10); //比較したいデータ
System.out.println("比較結果:" + suuCmp.equals(a[iFound]));
}
}
以下にコンパイルして実行する例を示します
D:\java>javac -source 1.4 Test.java D:\java>java Test 3番目レコード 商品コード:B01 数量:10 単価:1000 比較結果:true D:\java>