- PR -

Beanの比較について2

投稿者投稿内容
OZ
常連さん
会議室デビュー日: 2006/02/27
投稿数: 45
投稿日時: 2006-05-12 16:17
お世話になってます。前回、Beanの比較についてでアドバイス頂いたののですが
今回も助言頂ければと思います。

今回実現したい事は、Beanの配列を作成し、その配列を特定のフィールドで
ソートする事なのですがソートの実行方法として、
1.指定されたフィールドに対するソートを行う
2.指定された2つのフィールドに対するソートを行う
を満たしたいのです。

コード:
1 : public Class BeanSamp() {
2 :   private int height = 0;
3 :   private int weight = 0;
4 :   private String blood;
5 :   
6 :   public setHeight(int i){
7 :     height = i;
8 :   }
9 : 
10:   public setWeight(int i){
11:     weight = i;
12:   }
13: 
14:   public setBlood(String s){
15:     blood = s;
16:   }
17:   
18:   public void getHeight(){
19:     return height;
20:   }
21: 
22:   public void getWeight(){
23:     return weight;
24:   }
25: 
26:   public void getBlood(){
27:     return blood;
28:   }
29: 
30: }
31: 
32: class beanComp1 implements Comparator() {
33:   public int compare(Object o1,Object o2){
34:     //BeanSampのheightを昇順で比較してintをリターンする実装クラス
35:   }
36: }



この例でいいますと、予めフィールド「height」でソートする事が分かっていれば、
height用のComparator実装クラス「beanComp1」を準備する事で解決します。

これが、「指定されたフィールド」でソートとなると
 ・1.の場合、さらに「weight」「blood」用のComparator実装クラスをそれぞれ準備する
 ・2.の場合、2つのフィールドの組み合わせ数分(例の場合6つ)のComparator実装を準備する
Arrays.sortの引数に指定されたフィールド用の上記Comparator実装クラスを渡す事しか
思いつきません。

1つのComparator実装クラスで比較対照を動的に変更する事は可能でしょうか?
(これが不可能であると、例えば10個のフィールドを持つクラスで指定された3つの
 フィールドに対するソートを実行したい場合は、10x9x8=720個のComparator実装クラスを
 準備しなければならない事になる・・・)

宜しくお願い致します。
びしばし
大ベテラン
会議室デビュー日: 2002/03/13
投稿数: 181
投稿日時: 2006-05-12 16:32
リフレクションでできません ?
Class#getField(String)で名前指定できますし、
Class#getFields()で得た全フィールドから2個でも3個でも組み合わせればいいと思いますよ。

Jakarta Commons-BeanUtilsですでに実装されているかもしれません(私は詳しく見ていないので知りません)。
http://jakarta.apache.org/commons/beanutils/

[ メッセージ編集済み 編集者: びしばし 編集日時 2006-05-12 16:46 ]
yamasa
ベテラン
会議室デビュー日: 2003/02/15
投稿数: 80
投稿日時: 2006-05-12 17:18
コード:
public class CompositeComparator<T> implements Comparator<T> {
  private final Comparator<? super T>[] comparators;
  public CompositeComparator(Comparator<? super T>... comparators) {
    this.comparators = comparators;
  }
  public int compare(T o1, T o2) {
    for (Comparator<? super T> comparator : comparators) {
      int tmp = comparator.compare(o1, o2);
      if (tmp != 0) return tmp;
    }
    return 0;
  }
}


こんな感じのクラスを作って、
コード:
Comparator<BeanSamp> CMP_HEIGHT = new Comparator<BeanSamp>() {
  public int compare(BeanSamp o1, BeanSamp o2) {
    return o1.getHeight() - o2.getHeight();
  }
};
Comparator<BeanSamp> CMP_WEIGHT = new Comparator<BeanSamp>() {
  public int compare(BeanSamp o1, BeanSamp o2) {
    return o1.getWeight() - o2.getWeight();
  }
};
Comparator<BeanSamp> CMP_BLOOD = new Comparator<BeanSamp>() {
  public int compare(BeanSamp o1, BeanSamp o2) {
    return o1.getBlood().compareTo(o2.getBlood());
  }
};


こんな風に基本となるComparatorを用意しておけば、
コード:
Comparator<BeanSamp> cmp_hwb = new CompositeComparator<BeanSamp>(CMP_HEIGHT, CMP_WEIGHT, CMP_BLOOD);


こんな感じに任意の組み合わせのComparatorを作れます。
OZ
常連さん
会議室デビュー日: 2006/02/27
投稿数: 45
投稿日時: 2006-05-12 18:25
びしばしさん、ありがとうございます。

恥ずかしながらリフレクションという言葉は初めて知ったので、少し調べてみます。
引用:

Jakarta Commons-BeanUtilsですでに実装されているかもしれません(私は詳しく見ていないので知りません)。
http://jakarta.apache.org/commons/beanutils/


についても調べてみます。
(リフレクションについての概念は調べたのですが、取得したフィールドの型をキャストする方法と、それらの値を比較する方法を調べています)

yamasaさん、ありがとうございます。
これまた恥ずかしいのですが、
引用:

コード:
public class CompositeComparator<T> implements Comparator<T> {
  private final Comparator<? super T>[] comparators;
・・・

public class CompositeComparator<T> implements Comparator<T> {
・・・




というのは構文の表し方でしょうか。

申し訳ございません、宜しくお願い致します。
yamasa
ベテラン
会議室デビュー日: 2003/02/15
投稿数: 80
投稿日時: 2006-05-13 18:47
引用:

コード:
public class CompositeComparator<T> implements Comparator<T> {
  private final Comparator<? super T>[] comparators;
・・・

public class CompositeComparator<T> implements Comparator<T> {
・・・


というのは構文の表し方でしょうか。


Genericsという構文ですね。まあ、ここら辺は本質的じゃないんで、こういう書き方もあるんだ程度の認識でも構いません。
重要なのは、「内部にComparatorの配列を持ち、そのComparatorで順に比較した結果を返す新たなComparator」というのを定義することで、基本的なComparatorから複雑なComparatorを動的に生成することができる、ということです。
OZ
常連さん
会議室デビュー日: 2006/02/27
投稿数: 45
投稿日時: 2006-05-14 14:29
引用:

Genericsという構文ですね。まあ、ここら辺は本質的じゃないんで、こういう書き方もあるんだ程度の認識でも構いません。
重要なのは、「内部にComparatorの配列を持ち、そのComparatorで順に比較した結果を返す新たなComparator」というのを定義することで、基本的なComparatorから複雑なComparatorを動的に生成することができる、ということです。


yamasaさん、ありがとうございます。
まだ解決はしていないのですが、取り急ぎGenericsというキーワードで調べる事が
できた事をご報告致します。
OZ
常連さん
会議室デビュー日: 2006/02/27
投稿数: 45
投稿日時: 2006-05-15 18:00
追加で質問させて下さい。

引用:

yamasaさんの書き込み (2006-05-12 17:18) より:
コード:
1 : public class CompositeComparator<T> implements Comparator<T> {
2 :   private final Comparator<? super T>[] comparators;
3 :   public CompositeComparator(Comparator<? super T>... comparators) {
4 :     this.comparators = comparators;
5 :   }
6 :   public int compare(T o1, T o2) {
7 :     for (Comparator<? super T> comparator : comparators) {
8 :       int tmp = comparator.compare(o1, o2);
9 :       if (tmp != 0) return tmp;
10:     }
11:     return 0;
12:   }
13: }
14: 
15: Comparator<BeanSamp> CMP_HEIGHT = new Comparator<BeanSamp>() {
16:   public int compare(BeanSamp o1, BeanSamp o2) {
17:     return o1.getHeight() - o2.getHeight();
18:   }
19: };
20: Comparator<BeanSamp> CMP_WEIGHT = new Comparator<BeanSamp>() {
21:   public int compare(BeanSamp o1, BeanSamp o2) {
22:     return o1.getWeight() - o2.getWeight();
23:   }
24: };
25: Comparator<BeanSamp> CMP_BLOOD = new Comparator<BeanSamp>() {
26:   public int compare(BeanSamp o1, BeanSamp o2) {
27:     return o1.getBlood().compareTo(o2.getBlood());
28:   }
29: };
30: 
31: Comparator<BeanSamp> cmp_hwb = new CompositeComparator<BeanSamp>(CMP_HEIGHT, CMP_WEIGHT, CMP_BLOOD);
32: 



こんな感じに任意の組み合わせのComparatorを作れます。



7〜11までの処理が理解できなかったのですが、これは複数のComaprator実装を組み合わせて
実行するCompositeComparatorが、コンストラクターで受け取ったComparator配列のcompareを
順次実行するという認識で良いでしょうか?

これは意味的に、CMP_HEIGHT、CMP_WEIGHT、CMP_BLOODを個別に順次実行するのと同じなのでしょうか?

コード:
1 : SampBean[] beans;
2 : ・・・
3 : Arrays.sort(beans,CMP_HEIGHT);
4 : Arrays.sort(beans,CMP_WEIGHT);
5 : Arrays.sort(beans,CMP_BLOOD);



見当違いな事を言ってるようでしたらご指摘下さい。
宜しくお願い致します。
Kazuki
ぬし
会議室デビュー日: 2004/10/13
投稿数: 298
投稿日時: 2006-05-15 18:20
違います。
コード:
public class SampComparator implements Comparator<BeanSamp> {
	private Comparator<BeanSamp> COMP_HEIGHT = new ...(略);
	private Comparator<BeanSamp> COMP_WEIGHT = new ...(略);
	private Comparator<BeanSamp> COMP_BLOOD = new ...(略);
	
	public int compare(BeanSamp o1, BeanSamp o2) {
		int compHeight = COMP_HEIGHT.compare(o1, o2);
		if(compHeight != 0) {
		  return compHeight;
		}
		
		int compWeight = COMP_WEIGHT.compare(o1, o2);
		if(compWeight != 0) {
			return compWeight;
		}
		
		return COMP_BLOOD.compare(o1, o2);
	}
}

Arrays.sort(beans, new SampComparator());



ベタベタに書くとこんな感じです。
まず、heightで比較。
heightで比較した結果が等しくない場合は、heightの比較結果を返す。
heightが等しい場合は、weightで比較。
weightで比較した結果が等しくない場合は、weightの比較結果を返す。
最後にbloodで比較。
bloodの比較結果を返す。
という動きです。

このComparatorを使うと背の順。背が同じ人は体重の順。体重も同じ人は血液型順で並びます。


OZさんの示した
コード:
1 : SampBean[] beans;
2 : ・・・
3 : Arrays.sort(beans,CMP_HEIGHT);
4 : Arrays.sort(beans,CMP_WEIGHT);
5 : Arrays.sort(beans,CMP_BLOOD);


は、結局血液型の順番にしかなりません。

スキルアップ/キャリアアップ(JOB@IT)