/*
* Copyright(c) 2016. Your Techonology Partner(YTP). All rights reserved.
*
* このプログラムの著作権はYour Techonology Partner(YTP)が保有します。
* このプログラムはアパッチソフトウェアライセンスに従って配布します。
* このプログラムを再配布あるいは改造する場合は、上記著作権表示を必ず
* 含めるようにして下さい。免責事項も同ライセンスに準じます。詳細は
* http://www.apache.org/LICENSE を参照して下さい。
*
* Your Techonology Partner(YTP) owns the copyright of this program.
* This program is distributed following The Apache Software License.
* Redistribution and reproducing must contain above copyright.
* Disclaimer also follows The Apache Software License. Redistributor
* can refer to http://www.apache.org/LICENSE for further details.
*/
package jp.ne.ytp.util.tree;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Stack;
/**
* 複数のキーを持つことが可能な二分探索木のクラスです。
* 当クラスを利用する場合、{@link TreeActionListener}
* インタフェースを実装したクラスが必要になります。
* このクラスが管理するのは各階層のキーを持つノードと、
* TreeActionListenerに対する呼び出しのタイミングだけです。
* 各ノードが管理するデータオブジェクトとそれに対する処理
* (例えば、金額の集計やファイルソート、あるいは画面表示や帳票出力など)は、
* TreeActionListenerを実装したクラスで行って下さい。
* 当クラスではそのような処理は一切提供しませんが、
* この形態により、機能と実装の分離が可能となっています。
* 特にキーのマッチングに使う場合、
* キーが一致した時の処理を{@link TreeActionListener#match(int, Object)}メソッドに記述し、
* データを出力する処理を{@link TreeActionListener#sort(Object[], int, Object)}メソッドに記述すると、
* 効率よく実装することが可能になります。
* マッチング処理の場合、{@link TreeActionListener#makeHeader(Object, int, Object)}と
* {@link TreeActionListener#makeFooter(Object, int, Object)}の実装は不要です。
* 1階層分の情報を取得する{@link MultiKeyTree#keys(Object[])}や{@link MultiKeyTree#values(Object[])}
* メソッドを利用する場合には、
* {@link OneLayerListener}インタフェースを実装したクラスが必要になります。
* 【作者注】当クラスの実装は単純な二分探索木であるため、
* ソート済みのデータが登録された場合は極端な性能劣化が起きます。
* 近い将来、赤黒木などの平衡木に実装を置換する予定です。
* @see TreeActionListener OneLayerListener
* @see OneLayerListener
* @author YTP
*/
public class MultiKeyTree {
/**
* 昇順ソートを指定する際に使用します。
*/
public static final boolean ASC = true;
/**
* 降順ソートを指定する際に使用します。
*/
public static final boolean DESC = false;
/**
* ルートノードです。
*/
private MultiKeyTreeNode root_;
/**
* 各レベルのキーを格納するスタックです。
*/
private List> nodeStacks_ = new ArrayList<>();
/**
* キーの最下層番号です。
*/
private int iBottom_;
/**
* 全階層のソート順です。
*/
private boolean[] orders_;
/**
* 全階層のキーです。
*/
private Object[] keys_ = null;
/**
* イベントを通知するアクションリスナーです。
*/
private TreeActionListener listener_ = null;
/**
* 1階層アクションリスナーです。
*/
private OneLayerListener oneLayerListener_ = null;
/**
* 1階層アクションリスナーです。
*/
private InternalOneLayer myListener_ = new InternalOneLayer();
/**
* 全階層分のコンパレータです。
*/
private List> comparators_ = null;
/**
* デフォルトコンストラクタは使用しないで下さい。
*/
protected MultiKeyTree() {
}
/**
* iLayerで指定された階層のキーを持つMultiKeyTreeクラスインスタンスを生成します。
* @param iLayer ソートキーの最大レベル
*/
public MultiKeyTree(int iLayer) {
this(iLayer, new boolean[0]);
}
/**
* iLayerで指定された階層のキーと、
* ordersで指定されたソート順を持つMultiKeyTreeクラスインスタンスを生成します。
* @param layer 木の階層数
* @param orders 各レベルのソート順 昇順:{@link MultiKeyTree#ASC} 降順:{@link MultiKeyTree#DESC}
*/
public MultiKeyTree(int layer, boolean[] orders) {
// ルートノードの作成
root_ = new MultiKeyTreeNode("", 0);
iBottom_ = layer;
keys_ = new Object[iBottom_];
orders_ = new boolean[iBottom_];
for (int i = 0; i < iBottom_; i++) {
nodeStacks_.add(new Stack
TreeActionListener.java
/*
* Copyright(c) 2016. Your Technology Partner(YTP). All rights reserved.
*
* このプログラムの著作権はYour Technology Partner(YTP)が保有します。
* このプログラムはアパッチソフトウェアライセンスに従って配布します。
* このプログラムを再配布あるいは改造する場合は、上記著作権表示を必ず
* 含めるようにして下さい。免責事項も同ライセンスに準じます。詳細は
* http://www.apache.org/LICENSE を参照して下さい。
*
* Your Technology Partner(YTP) owns the copyright of this program.
* This program is provided in conformity with The Apache Software
* License agreement. Redistribution and reproduction must contain
* the above copyright notice. The Disclaimer is also based on
* The Apache Software License. Redistributor can refer to
* http://www.apache.org/LICENSE for further details.
*/
package jp.ne.ytp.util.tree;
/**
* {@link MultiKeyTree}クラスからイベントを通知するためのメソッドを規定するインタフェースです。
* {@link MultiKeyTree}クラスを利用する場合は、このインタフェースを実装したクラスが必要です。<br>
* 大きく分けると、
* <ol>
* <li>木を作成する際に呼び出されるメソッド
* <ul>
* <li>{@link TreeActionListener#initializeRoot()}</li>
* <li>{@link TreeActionListener#create(int)}</li>
* <li>{@link TreeActionListener#match(int, Object)}</li>
* </ul>
* </li>
* <li>木を走査(トラバース)する際に呼び出されるメソッド
* <ul>
* <li>{@link TreeActionListener#makeHeader(Object, int, Object)}</li>
* <li>{@link TreeActionListener#sort(Object[], int, Object)}</li>
* <li>{@link TreeActionListener#makeFooter(Object, int, Object)}</li>
* </ul>
* </li>
* </ol>
* の2種類があります。
* @see MultiKeyTree
* @author YTP
*/
public interface TreeActionListener extends java.util.EventListener {
/*** 木作成時の処理 ***/
/**
* ルートノードを初期化するために呼び出されます。
* このメソッドを実装する場合、ルートノード(階層番号0)用のデータオブジェクトを生成し、
* そのオブジェクトを返すようにして下さい。
* このメソッドは、{@link MultiKeyTree}クラスを利用する側が、
* {@link MultiKeyTree#addListener(TreeActionListener)}を呼んだ時に一度だけ呼び出されます。
* @return ルートノードが持つデータオブジェクト
*/
public Object initializeRoot();
/**
* まだ存在しないキーを持つノードが新たに生成された場合に呼び出されるメソッドです。
* このメソッドを実装する場合、iLayerを判断して適切なデータオブジェクトを生成し、
* そのオブジェクトを返すようにして下さい。
* 各階層で新規のノード(キー)が生成された場合は、
* このメソッドが必ず呼び出されることに注意して下さい。
* @param iLayer 生成したノードの階層番号(1オリジン ただしルートノードの場合のみ0)
* @return ノードが管理するデータオブジェクト
*/
public Object create(int iLayer);
/**
* 同一階層に同一キーのノードがすでに存在する場合に呼び出されます。
* このメソッドを実装する場合、iLayerを判断して、
* nodeDataで渡されたデータオブジェクトに対して適切な処理を行い、
* 再びそのオブジェクトを返すようにして下さい。
* 階層番号が0であるルートノードを含み、
* 各階層でこのメソッドが呼び出されることに注意して下さい。
* @param iLayer 一致したノードの階層番号(1オリジン ただしルートノードの場合のみ0)
* @param nodeData ノードが管理するデータオブジェクト
* @return ノードが管理するデータオブジェクト
*/
public Object match(int iLayer, Object nodeData);
/*** トラバース時の処理 ***/
/**
* 木の走査時に最下層のノードで呼び出されるメソッドです。
* keysは1レコード分に相当する全階層のキー配列です。
* nodeDataは最下層ノードが管理するデータオブジェクトです。
* {@link TreeActionListener#makeHeader(Object, int, Object)}と
* {@link TreeActionListener#makeHeader(Object, int, Object)}を使わずに、
* このメソッドのみを実装すると、ソートされた明細として出力することが可能となります。
* @param keys 最上位階層から最下層までのキー配列
* @param iLayer 最下層の階層数
* @param nodeData 最下層ノードが管理するデータオブジェクト
*/
public void sort(Object[] keys, int iLayer, Object nodeData);
/**
* 木の走査時に、最下層を除く各ノードのヘッダ部分(行きがけ)で呼び出されるメソッドです。
* @param key ノードのキー
* @param iLayer ノードの階層番号(1オリジン、ただしルートノードの場合は0)
* @param nodeData ノードが管理するデータオブジェクト
*/
public void makeHeader(Object key, int iLayer, Object nodeData);
/**
* 木の走査時に、最下層を除く各ノードのフッタ部分(帰りがけ)で呼び出されるメソッドです。
* @param key ノードのキー
* @param iLayer ノードの階層番号(1オリジン、ただしルートノードの場合は0)
* @param nodeData ノードが管理するデータオブジェクト
*/
public void makeFooter(Object key, int iLayer, Object nodeData);
}
MultiKeyTreeNode.java
/*
* Copyright(c) 2016. Your Technology Partner(YTP). All rights reserved.
*
* このプログラムの著作権はYour Technology Partner(YTP)が保有します。
* このプログラムはアパッチソフトウェアライセンスに従って配布します。
* このプログラムを再配布あるいは改造する場合は、上記著作権表示を必ず
* 含めるようにして下さい。免責事項も同ライセンスに準じます。詳細は
* http://www.apache.org/LICENSE を参照して下さい。
*
* Your Technology Partner(YTP) owns the copyright of this program.
* This program is provided in conformity with The Apache Software
* License agreement. Redistribution and reproduction must contain
* the above copyright notice. The Disclaimer is also based on
* The Apache Software License. Redistributor can refer to
* http://www.apache.org/LICENSE for further details.
*/
package jp.ne.ytp.util.tree;
import java.util.*;
import jp.ne.ytp.util.*;
/**
* 多階層型二分探索木のノードです。
* 各ノードインスタンスは次の情報を持ちます。
* <ol>
* <li>左右ノードへの枝</li>
* <li>下階層への枝</li>
* <li>階層番号を表す1オリジンの整数</li>
* <li>ノードのキーを表現する文字列</li>
* <li>ノードのデータを表現するオブジェクト</li>
* <li>ノードキーの大小比較をするためのコンパレータ</li>
* </ol>
* @see TreeNode
* @author YTP
*/
class MultiKeyTreeNode extends TreeNode {
/**
* 下階層のノードです。
*/
MultiKeyTreeNode lowerNode;
/**
* ノードの階層番号です。
*/
private int iLayer_;
/**
* ノードのデータオブジェクトです。
*/
private Object value_;
/**
* ノードキー大小比較用のコンパレータです。
*/
private Comparator comp_ = null;
/**
* ノードのキーとしてkeyを、階層としてiLayerを持つノードインスタンスを生成します。
* コンパレータは標準コンパレータ{@link DefaultComparator}を使用します。
* @param key キー
* @param iLayer 階層番号
*/
MultiKeyTreeNode(Object key, int iLayer) {
this(key, iLayer, DefaultComparator.getComparator());
}
/**
* ノードのキーとしてkeyを、階層としてiLayerを、
* コンパレータとしてcompを持つノードインスタンスを生成します。
* @param key キー
* @param iLayer 階層番号
* @param comp コンパレータ
*/
MultiKeyTreeNode(Object key, int iLayer, Comparator comp) {
key_ = key;
iLayer_ = iLayer;
comp_ = comp;
}
/**
* ノードキー大小比較用のコンパレータを設定します。
* @param comp コンパレータ
*/
void setComparator(Comparator comp) {
comp_ = comp;
}
/**
* このインスタンスのキーとcompNodeのキーを比較した結果を返します。
* 比較には標準コンパレータ({@link DefaultComparator})あるいは、
* コンストラクタ{@link MultiKeyTreeNode(Object, int, Comparator)}または
* {@link MultiKeyTreeNode#setComparator(Comparator)}で設定されたコンパレータを使用します。
* @param compNode 比較対照のノード
*/
int compare(TreeNode compNode) {
return comp_.compare(this.key_, compNode.key_); // fixed forgotten key. 20160505
}
/**
* このノードインスタンスが管理するデータオブジェクトを設定します。
* @param value データオブジェクト
*/
void setData(Object value) {
value_ = value;
}
/**
* このノードインスタンスが管理するデータオブジェクトを返します。
*/
Object getData() {
return value_;
}
/**
* このノードインスタンスのキーを返します。
*/
Object getKey() {
return key_;
}
/**
* このノードインスタンスのキーの階層番号を1オリジンで返します。
* @return このノードの階層番号
*/
int getLayer() {
return iLayer_;
}
/**
* このノードキーの文字列表現を返します。
* @return このノードキーの文字列表現
*/
public String toString() {
return key_.toString();
}
}
TreeNode.java
/*
* Copyright(c) 2016. Your Technology Partner(YTP). All rights reserved.
*
* このプログラムの著作権はYour Technology Partner(YTP)が保有します。
* このプログラムはアパッチソフトウェアライセンスに従って配布します。
* このプログラムを再配布あるいは改造する場合は、上記著作権表示を必ず
* 含めるようにして下さい。免責事項も同ライセンスに準じます。詳細は
* http://www.apache.org/LICENSE を参照して下さい。
*
* Your Technology Partner(YTP) owns the copyright of this program.
* This program is provided in conformity with The Apache Software
* License agreement. Redistribution and reproduction must contain
* the above copyright notice. The Disclaimer is also based on
* The Apache Software License. Redistributor can refer to
* http://www.apache.org/LICENSE for further details.
*/
package jp.ne.ytp.util.tree;
import java.util.*;
/**
* 二分探索木のノード用基底クラスです。
* 左右ノードの参照のみを持ちます。<br>
* [未解決]単純な二分探索木であるためソートされたデータが来ると、
* 深刻な性能問題が起きます。赤黒木などの平衡木に換装する必要があります。
* @see MultiKeyTreeNode
* @author YTP
*/
abstract class TreeNode {
/**
* このノードよりキーが小さい場合に返す値です。
*/
static final int SMALLER = 1;
/**
* このノードよりキーが大きい場合に返す値です。
*/
static final int BIGGER = 2;
/**
* このノードとキーが同じ場合に返す値です。
*/
static final int BINGO = 0;
/**
* キーが見つからない場合に返す値です。
*/
static final int NOTFOUND = -1;
/**
* このインスタンスノードの左ノードです。
*/
TreeNode left_; // transfered from MultiKeyNode. 20160505
/**
* このインスタンスノードの右ノードです。
*/
TreeNode right_;
/**
* ノードキーです。
*/
Object key_;
/**
* ノード同士のキーを比較する際に使用する抽象化メソッドです。
*/
abstract int compare(TreeNode trNode);
/**
* newNodeノードを追加します。<br>
* newNodeのキーとインスタンスのキーをコンパレータにより比較し、
* <ol>
* <li>このインスタンスより小さいキーの場合
* <ul>
* <li>このインスタンスの左ノードが既にある場合はそのノード</li>
* <li>このインスタンスの左ノードがまだ無い場合はnewNodeを左にぶら下げてnull</li>
* </ul>
* </li>
* <li>このインスタンスより大きいキーの場合
* <ul>
* <li>このインスタンスの右ノードが既にある場合はそのノード</li>
* <li>このインスタンスの右ノードがまだ無い場合はnewNodeを右にぶら下げてnull</li>
* </ul>
* </li>
* <li>このインスタンスと同じキーの場合はnull</li>
* </ol>
* をそれぞれ返します。<br>
* 当メソッドは、内部で再帰を使用していません。
* そのため、呼び出し側で結果を判定しながらループさせる必要があります。
* @param newNode 追加対象のノード
* @return 追加を完了した場合はnull、引き続き処理が必要な場合はそのノード
*/
TreeNode append(TreeNode newNode) {
int iComparison;
iComparison = compare(newNode);
// このノードと同じ
if (iComparison == 0) {
// 追加終了
return null;
// このノードより小さい
} else if (iComparison > 0) {
// 左ノードがぶら下がっている
if (left_ != null) {
return left_;
} else {
left_ = newNode;
// 追加終了
return null;
}
// このノードより大きい
} else {
// 右ノードがぶら下がっている
if (right_ != null) {
return right_;
} else {
right_ = newNode;
// 追加終了
return null;
}
}
}
/**
* targetNodeノードを検索します。
* 当メソッドは再帰を利用しているためStackOverFlowエラーが発生する危険があり、
* 使用しないで下さい。
* @deprecated
*/
TreeNode find(TreeNode targetNode) {
int iComparison;
iComparison = compare(targetNode);
// このノードと同じ
if (iComparison == 0) {
return this;
// このノードより小さい
} else if (iComparison > 0) {
if (left_ != null) {
return left_.find(targetNode);
} else {
return null;
}
// このノードより大きい
} else {
if (right_ != null) {
return right_.find(targetNode);
} else {
return null;
}
}
}
/**
* nodeContainerの先頭要素のノードを検索します。<br>
* nodeContainerの先頭要素のノードのキーとインスタンスのキーをコンパレータにより比較し、
* <ol>
* <li>このインスタンスより小さいキーの場合<br>
* nodeContainerの先頭要素としてこのインスタンスの左ノードを設定(上書き)し、
* {@link TreeNode#SMALLER}を返します。
* </li>
* <li>このインスタンスより大きいキーの場合<br>
* nodeContainerの先頭要素としてこのインスタンスの右ノードを設定(上書き)し、
* {@link TreeNode#BIGGER}を返します。
* </li>
* <li>このインスタンスと同じキーの場合<br>
* nodeContainerの先頭要素としてこのインスタンスを設定(上書き)し、
* {@link TreeNode#BINGO}を返します。
* </li>
* <li>見つからなかった場合<br>
* {@link TreeNode#NOTFOUND}を返します。
* </li>
* </ol>
* 当メソッドは、内部で再帰を使用していません。
* そのため、呼び出し側で結果を判定しながらループさせる必要があります。
* @param nodeContainer 検索対象のノードを先頭要素として格納したリスト
* @return SMALLER/BIGGER/BINGO/NOTFOUND のいずれか
*/
int find(List nodeContainer) {
int iComparison;
iComparison = compare((TreeNode)nodeContainer.get(0));
//iComparison = comp_.compare(this, nodeContainer.get(0));
// このノードと同じ
if (iComparison == 0) {
nodeContainer.set(0, this);
return BINGO;
// このノードより小さい
} else if (iComparison > 0) {
if (left_ != null) {
nodeContainer.set(0, left_);
return SMALLER;
} else {
return NOTFOUND;
}
// このノードより大きい
} else {
if (right_ != null) {
nodeContainer.set(0, right_);
return BIGGER;
} else {
return NOTFOUND;
}
}
}
}
OneLayerListener.java
/*
* Copyright(c) 2016. Your Technology Partner(YTP). All rights reserved.
*
* このプログラムの著作権はYour Technology Partner(YTP)が保有します。
* このプログラムはアパッチソフトウェアライセンスに従って配布します。
* このプログラムを再配布あるいは改造する場合は、上記著作権表示を必ず
* 含めるようにして下さい。免責事項も同ライセンスに準じます。詳細は
* http://www.apache.org/LICENSE を参照して下さい。
*
* Your Technology Partner(YTP) owns the copyright of this program.
* This program is provided in conformity with The Apache Software
* License agreement. Redistribution and reproduction must contain
* the above copyright notice. The Disclaimer is also based on
* The Apache Software License. Redistributor can refer to
* http://www.apache.org/LICENSE for further details.
*/
package jp.ne.ytp.util.tree;
/**
* {@link MultiKeyTree}クラスの1階層分の走査を行なう際に、
* イベント通知するためのメソッドを規定するインタフェースです。
* @see MultiKeyTree
* @see TreeActionListener
* @author YTP
*/
public interface OneLayerListener extends java.util.EventListener {
/**
* 1階層分の走査を実行する際に、各ノードごとに呼び出すメソッドです。
* このメソッドを実装する側で出力処理等を記述して下さい。
* @param key ノードのキー
* @param value ノードのデータ
*/
public void sortOneLayer(Object key, Object value);
}
InternalOneLayer.java
/*
* Copyright(c) 2016. Your Technology Partner(YTP). All rights reserved.
*
* このプログラムの著作権はYour Technology Partner(YTP)が保有します。
* このプログラムはアパッチソフトウェアライセンスに従って配布します。
* このプログラムを再配布あるいは改造する場合は、上記著作権表示を必ず
* 含めるようにして下さい。免責事項も同ライセンスに準じます。詳細は
* http://www.apache.org/LICENSE を参照して下さい。
*
* Your Technology Partner(YTP) owns the copyright of this program.
* This program is provided in conformity with The Apache Software
* License agreement. Redistribution and reproduction must contain
* the above copyright notice. The Disclaimer is also based on
* The Apache Software License. Redistributor can refer to
* http://www.apache.org/LICENSE for further details.
*/
package jp.ne.ytp.util.tree;
import java.util.*;
/**
* {@link MultiKeyTree}クラスが1階層分の走査を行なう際に、
* 内部的に使用する{@link OneLayerListener}の実装クラスです。
* @see MultiKeyTree OneLayerListener
* @author YTP
*/
class InternalOneLayer implements OneLayerListener {
/**
* キーの集合です。
*/
private List keys_ = null;
/**
* 値の集合です。
*/
private List values_ = null;
/**
* キーと値を保存します。
* このメソッドは{@link OneLayerListener}インタフェースの実装です。
* このメソッドを実装する側で出力処理等を記述して下さい。
* @param key ノードのキー
* @param value ノードのデータ
*/
public void sortOneLayer(Object key, Object value) {
keys_.add(key);
values_.add(value);
}
/**
* キーと値の保存領域を初期化します。
*/
void init() {
keys_ = new ArrayList();
values_ = new ArrayList();
}
/**
* 保存したキーの集合を返します。
* @return キーの集合
*/
List getKeys() {
return keys_;
}
/**
* 保存した値の集合を返します。
* @return 値の集合
*/
List getValues() {
return values_;
}
}
DefaultComparator.java
/*
* Copyright(c) 2016. Your Technology Partner(YTP). All rights reserved.
*
* このプログラムの著作権はYour Technology Partner(YTP)が保有します。
* このプログラムはアパッチソフトウェアライセンスに従って配布します。
* このプログラムを再配布あるいは改造する場合は、上記著作権表示を必ず
* 含めるようにして下さい。免責事項も同ライセンスに準じます。詳細は
* http://www.apache.org/LICENSE を参照して下さい。
*
* Your Technology Partner(YTP) owns the copyright of this program.
* This program is provided in conformity with The Apache Software
* License agreement. Redistribution and reproduction must contain
* the above copyright notice. The Disclaimer is also based on
* The Apache Software License. Redistributor can refer to
* http://www.apache.org/LICENSE for further details.
*/
package jp.ne.ytp.util;
import java.util.Comparator;
/**
* キーの比較に使用する標準のコンパレータです。
* 比較対象の2つのオブジェクトが{@link Comparable}Comparableインタフェースを実装している場合は{@link Comparable#compareTo(Object)}メソッドによる比較結果を、
* 実装していない場合はそのオブジェクトの文字列表現(toString())を使用して辞書順比較を行います。
* コンパレータを取得する際には{@link DefaultComparator#getComparator()}
* メソッドを呼び出して下さい。
* 当クラスはシングルトンデザインパターンで実装されています。
* @see java.util.Comparator
* @author YTP
*/
public class DefaultComparator implements Comparator {
/**
* 唯一のDefaultComparatorインスタンスです。
*/
private static DefaultComparator comp_ = new DefaultComparator();
/**
* デフォルトコンストラクタは使用できません。
*/
private DefaultComparator() {
}
/**
* 標準のコンパレータを返します。
*/
public static Comparator getComparator() {
return comp_;
}
/**
* 指定された2つのオブジェクトが{@link Comparable}Comparableインタフェースを実装している場合は{@link Comparable#compareTo(Object)}メソッドによる比較結果を、
* 実装していない場合はそのオブジェクトの文字列表現(toString())の比較結果を(つまり辞書順比較)返します。
* 文字列表現による比較はそれぞれの文字のUnicode値に基づいて行われます。
* @param o1 比較元のオブジェクト
* @param o2 比較相手のオブジェクト
* @return 比較した結果
* <ul>
* <li>o1がo2より小さい場合は負の値</li>
* <li>o1がo2より大きい場合は正の値</li>
* <li>o1がo2と等しい場合は0</li>
* </ul>
*/
@SuppressWarnings("unchecked")
public int compare(Object o1, Object o2) {
if (o1 instanceof Comparable && o2 instanceof Comparable) {
return ((Comparable)o1).compareTo(o2);
} else {
return o1.toString().compareTo(o2.toString());
}
}
}