Y's Jlibのソースコード
 

MultiKeyTree.java
/*
 *  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());
            try {
                orders_[i] = orders[i]; // ソート順の指定
            } catch (ArrayIndexOutOfBoundsException e) {
                orders_[i] = ASC;       // 指定が無い場合は全て昇順
            }
        }
    }
    
    /**
     *  このクラスからイベントを通知するアクションリスナーを登録します。
     *  このメソッドの中から、{@link TreeActionListener#initializeRoot()}メソッドが呼び出されます。
     *  @param listener アクションリスナー
     */
    public void addListener(TreeActionListener listener) {
        listener_ = listener;
        root_.setData(listener_.initializeRoot());
    }
    
    /**
     *  1階層走査においてこのクラスからイベントを通知するアクションリスナーを登録します。
     *  @param listener アクションリスナー
     */
    public void addOneLayerListener(OneLayerListener listener) {
        oneLayerListener_ = listener;
    }
    
    /**
     *  全階層分のコンパレータを設定します。
     *  キーを比較する規則を階層ごとに変えたい場合や、
     *  標準のコンパレータ(辞書順比較)を使わない場合に、
     *  当メソッドにてコンパレータを設定して下さい。
     *  全階層で標準コンパレータを使用する場合、当メソッドの呼び出しは不要です。
     *  @param comparators 全階層分のコンパレータ
     *  @see jp.ne.ytp.util.DefaultComparator
     */
    public void setComparators(List> comparators) {
        comparators_ = comparators;
    }
    
    /**
     *  キーを木に登録します。
     *  このメソッドの内部から、
     *  {@link TreeActionListener#create(int)}または
     *  {@link TreeActionListener#match(int, Object)}が呼び出されます。
     *  @param keys キーの配列
     */
    public void put(Object[] keys) {
        this.regist(keys, keys.length);
    }
    
    /**
     *  キーを木に登録するための内部処理です。
     *  @param keys キー配列
     *  @param keyNum キーの数=レベル数
     *  @return 作成または更新されたノード
     */
    private MultiKeyTreeNode regist(Object[] keys, int keyNum) {
        int p = 0;
        int j = 0;
        MultiKeyTreeNode currNode = null;
        MultiKeyTreeNode resultNode = null;
        MultiKeyTreeNode newNode = null;
        MultiKeyTreeNode tempNode = null;
        int iRet = 0;
        List nodeContainer = new ArrayList<>();
        
        nodeContainer.add(null);
        
        // キーの数を設定
        //iKeyNum = keys.length;
        
        // まずはルートノードから始める
        root_.setData(listener_.match(0, root_.getData()));
        currNode = root_;
        
        // 指定されたキーの数だけループ
        for (p = 0; p < keyNum; p++) {
            // 新規ノードの作成
            newNode = new MultiKeyTreeNode(keys[p], p);
            // コンパレータの設定
            if (comparators_ != null && comparators_.size() > p) {
                newNode.setComparator(comparators_.get(p));
            }
            
            tempNode = currNode;
            while (true) {      /*** スタックオーバーフロー対応 A010726 YT0050 ***/
                nodeContainer.set(0, newNode);
                // ノードの検索 検索の結果がaNodeに設定されて返ってくる
                iRet = tempNode.find(nodeContainer);
                
                // 見つかった
                if (iRet == MultiKeyTreeNode.BINGO) {
                    resultNode = (MultiKeyTreeNode)nodeContainer.get(0);
                    //System.out.println(resultNode.getKey());
                    break;
                // このキーよりも小さい
                } else if (iRet == MultiKeyTreeNode.SMALLER) {
                    tempNode = (MultiKeyTreeNode)nodeContainer.get(0);
                // このキーよりも大きい
                } else if (iRet == MultiKeyTreeNode.BIGGER) {
                    tempNode = (MultiKeyTreeNode)nodeContainer.get(0);
                // 見つからなかった
                } else if (iRet == MultiKeyTreeNode.NOTFOUND) {
                    resultNode = null;
                    break;
                }
            }
            
            // キーが見つからなかった
            if (resultNode == null) {
                // ノードを追加する
                newNode.setData(listener_.create(p + 1));
                // currNode.append(newNode);
                
                /*** スタックオーバーフロー対応 A011002 YT0050 ***/
                tempNode = currNode;
                while (tempNode != null) {
                    // ノードの追加
                    tempNode = (MultiKeyTreeNode)tempNode.append(newNode);
                }
                
                currNode = newNode;
                
                // 残りのキーの数だけループして新規ノードを作成する
                for (j = p + 1; j < keyNum; j++, p++) {
                    // 新規ノードの作成
                    newNode = new MultiKeyTreeNode(keys[j], j);
                    // コンパレータの設定 fixed. 20160505
                    if (comparators_ != null && comparators_.size() > j) {
                        newNode.setComparator(comparators_.get(j));
                    }
                    // 今のノードの下位レベルとして新しいノードを設定する
                    currNode.lowerNode = newNode;
                    
                    newNode.setData(listener_.create(j + 1));
                    currNode = newNode;
                }
            } else {
                resultNode.setData(listener_.match(p + 1, resultNode.getData()));    // リスナーの呼び出し A00/02/11
                
                // 下位レベルのノードが既に存在する
                if (resultNode.lowerNode != null) {
                    currNode = (MultiKeyTreeNode)resultNode.lowerNode;
                } else {
                    // 見つかったノードを設定
                    currNode = resultNode;
                    
                    // 残りのキーの数だけループして新規ノードを作成する
                    for (j = p + 1; j < keyNum; j++, p++) {
                        newNode = new MultiKeyTreeNode(keys[j], j);
                        // コンパレータの設定 fixed. 20160505
                        if (comparators_ != null && comparators_.size() > j) {
                            newNode.setComparator(comparators_.get(j));
                        }
                        // 新しいノードを今のノードの下位レベルとしてぶら下げる
                        currNode.lowerNode = newNode;
                        newNode.setData(listener_.create(j + 1));
                        currNode = newNode;
                    }
                }
            }
        }
        
        return newNode;
    }
    
    /**
     *  keysで指定されたキーに対応する値を返します。
     *  キーが見つからない場合nullを返します。
     *  @param keys キーの配列
     *  @return 指定されたキーに対応した値(該当なしの場合null)
     */
    public Object get(Object[] keys) {
        MultiKeyTreeNode node = find(keys, keys.length);
        
        if (node != null) {
            return node.getData();
        } else {
            return null;
        }
    }
    
    /**
     *  keysで指定されたキーを持つノードを検索します。
     *  ノードが見つからない場合nullを返します。
     *  @param keys キーの配列
     *  @param keyNum 検索に使用するキーの数(階層数)
     *  @return 指定されたキーを持つノード (該当なしの場合はnull)
     */
    private MultiKeyTreeNode find(Object[] keys, int keyNum) {
        int iI = 0;
        MultiKeyTreeNode returnNode = null;         // M97/04/30
        MultiKeyTreeNode currNode = null;
        MultiKeyTreeNode resultNode = null;
        MultiKeyTreeNode newNode = null;
        MultiKeyTreeNode tempNode = null;
        int iRet = 0;
        ArrayList nodeContainer = new ArrayList<>();
        
        nodeContainer.add(null);
        
        // キーの数を設定
        // R97/04/30 iKeyNum = keys.length;
        
        // まずはルートノードから始める
        currNode = root_;
        //currNode = (MultiKeyTreeNode)root_.right_;
        
        // 指定されたキーの数だけループ
        for (iI = 0; iI < keyNum; iI++) {
        //for (iI = 1; iI < iKeyNum; iI++) {  // A010427 MS7026
            // 新規ノードの作成
            newNode = new MultiKeyTreeNode(keys[iI], iI);
            
            // このキーを持つノードを探す
            //resultNode = (MultiKeyTreeNode)currNode.find(newNode);
            
            /*** スタックオーバーフロー対応 A011004 YT0050 ***/
            tempNode = currNode;
            while (true) {
                nodeContainer.set(0, newNode);
                // ノードの検索 検索の結果がaNodeに設定されて返ってくる
                iRet = tempNode.find(nodeContainer);
                
                // 見つかった
                if (iRet == MultiKeyTreeNode.BINGO) {
                    resultNode = (MultiKeyTreeNode)nodeContainer.get(0);
                    //System.out.println(resultNode.getKey());
                    break;
                // このキーよりも小さい
                } else if (iRet == MultiKeyTreeNode.SMALLER) {
                    tempNode = (MultiKeyTreeNode)nodeContainer.get(0);
                // このキーよりも大きい
                } else if (iRet == MultiKeyTreeNode.BIGGER) {
                    tempNode = (MultiKeyTreeNode)nodeContainer.get(0);
                // 見つからなかった
                } else if (iRet == MultiKeyTreeNode.NOTFOUND) {
                    resultNode = null;
                    break;
                }
            }
            
            // キーが見つかった〜
            if (resultNode != null) {
                // 最下位レベルではない
                if (iI < keyNum - 1) {
                    // 下位レベルのノードが存在する
                    if (resultNode.lowerNode != null) {
                        // 下位レベルのノードを検索する
                        currNode = (MultiKeyTreeNode)resultNode.lowerNode;
                    } else {
                        // 見つからん
                        returnNode = null;
                        break;
                    }
                } else {
                    // 見つかったからデータを返す
                    returnNode = resultNode;
                    break;
                }
            } else {
                // やめじゃやめじゃ〜
                returnNode = null;
                break;
            }
        }
        
        return returnNode;
    }
    
    /**
     *  ルートノードを含む全ノードを走査します。
     *  ノードを走査しながらアクションリスナーに対して、
     *  {@link TreeActionListener#makeHeader(Object, int, Object)} {@link TreeActionListener#sort(Object[], int, Object)}
     *  {@link TreeActionListener#makeFooter(Object, int, Object)}
     *  を随時呼び出しイベントを通知します。
     */
    public void traverse() {
        int keyLevel = 0;      // 最初は0レベルから
        
        // ヘッダ処理 A00/02/10
        listener_.makeHeader(root_.getKey(), keyLevel, root_.getData());
        
        if (root_.right_ != null) {
            if (orders_[keyLevel] == ASC) {
                traverseAsc((MultiKeyTreeNode)root_.right_, keyLevel);
            } else {
                traverseDesc((MultiKeyTreeNode)root_.right_, keyLevel);
            }
        }
        
        // フッタ処理 A00/02/10
        listener_.makeFooter(root_.getKey(), keyLevel, root_.getData());
    }
    
    /**
     *  指定されたキーを持つノード配下を走査します。
     *  ノードを走査しながらアクションリスナーに対して、
     *  {@link TreeActionListener#makeHeader(Object, int, Object)}・
     *  {@link TreeActionListener#sort(Object[], int, Object)}・
     *  {@link TreeActionListener#makeFooter(Object, int, Object)}
     *  を随時呼び出しイベントを通知します。
     *  @param keys キーの配列
     *  @exception NoSuchElementException 指定されたキーが見つからなかった場合
     */
    public void traverse(Object[] keys) {
        traverse(keys, keys.length);
    }
    
    /**
     *  指定されたキーと階層のノード配下を走査します。
     *  ノードを走査しながらアクションリスナーに対して、
     *  {@link TreeActionListener#makeHeader(Object, int, Object)}・
     *  {@link TreeActionListener#sort(Object[], int, Object)}・
     *  {@link TreeActionListener#makeFooter(Object, int, Object)}
     *  を随時呼び出しイベントを通知します。
     *  呼び出しの例:
     *  iLayer=2, keys[0]="001", keys[1]="B0", keys[2]="AA001" の場合、
     *  第1階層のキーが001・第2階層のキーがB0であるノードを開始点として走査します。
     *  第3階層以降のキーは無視されます。
     *  @param keys キーの配列
     *  @param layer 走査対象キーの階層数
     *  @exception NoSuchElementException 指定されたキーが見つからなかった場合
     */
    public void traverse(Object[] keys, int layer) {
        int iKeyLevel =layer;   // 開始レベルをセット
        
        // 指定したキーを持つ開始ノードを検索
        MultiKeyTreeNode startNode = this.find(keys, layer);
        
        // 指定したキーを持つノードが見つからなかった場合:NullPointerExceptionで返す
        if (startNode == null) {
            throw new NoSuchElementException();
        }
        
        // ヘッダ処理
        listener_.makeHeader(startNode.getKey(), iKeyLevel, startNode.getData());
        
        // 下位ノード
        if (startNode.lowerNode != null) {
            // パラメータで渡された上位キーをキースタックにつむ
                for (int i = 0; i < layer; i++) {
                    nodeStacks_.get(i).push(keys[i]);
                }
            // 下位のソート順が昇順であれば
            if (orders_[iKeyLevel] == ASC) {
                traverseAsc(startNode.lowerNode, iKeyLevel);    //昇順トラバース
            // 降順
            } else {
                traverseDesc(startNode.lowerNode, iKeyLevel);   //降順トラバース
            }
        }
        // フッタ処理 A00/02/10
        listener_.makeFooter(startNode.getKey(), iKeyLevel, startNode.getData());
    }
    
    /**
     *  木を昇順で走査します。
     *  @param currNode 走査を開始するノード
     *  @param layer 走査対象の階層
     */
    private void traverseAsc(MultiKeyTreeNode currNode, int layer) {
        Stack traversedNodes = new Stack<>();    // 当レベルのノードをスタックする
        
        /*** ループ版に変更(レベルを下げる時だけは再帰する) M011003 YT0050 ***/
        while (!(currNode == null && traversedNodes.empty())) {
            //System.out.println("STEP-00");
            
            // 左端まで行く
            while (currNode != null) {
                // ノードをPUSH
                traversedNodes.push(currNode);
                //System.out.println("PUSH " + currNode.getKey());
                // 左のノードを次のノードとして設定する
                currNode = (MultiKeyTreeNode)currNode.left_;
            }
            
            // 一つ前に戻る
            currNode = (MultiKeyTreeNode)traversedNodes.pop();
            
            /*
             * ここから主処理を書く
             */
             
            // キーを設定
            keys_[layer] = currNode.getKey();
            
            // 最下層でなければ
            if (layer < iBottom_ - 1 ) {
                // ヘッダ処理 A00/02/10
                listener_.makeHeader(currNode.getKey(), layer + 1, currNode.getData());
                // 下位レベルのノードがある
                if (currNode.lowerNode != null) {
                    if (orders_[layer + 1] == ASC) {
                        traverseAsc((MultiKeyTreeNode)currNode.lowerNode, layer + 1);     // 昇順
                    } else {
                        traverseDesc((MultiKeyTreeNode)currNode.lowerNode, layer + 1);    // 降順
                    }
                }
            }
            
            // 最下層の場合
            if (layer == iBottom_ - 1) {
                // 明細レベルの処理
                listener_.sort(keys_, layer + 1, currNode.getData());
            }
            
            // 最下層でなければ
            if (layer < iBottom_ - 1 ) {
                // フッタ処理 A00/02/10
                listener_.makeFooter(currNode.getKey(), layer + 1, currNode.getData());
            }
            
            // 右ノードの設定
            currNode = (MultiKeyTreeNode)currNode.right_;
        }
    }
    
    /**
     *  木を降順で走査します。
     *  @param currNode 走査を開始するノード
     *  @param layer 走査対象の階層
     */
    private void traverseDesc(MultiKeyTreeNode currNode, int layer) {
        Stack traversedNodes = new Stack<>();    // 当レベルのノードをスタックする
        
        /*** ループ版に変更(レベルを下げる時だけは再帰する) M011003 YT0050 ***/
        while (!(currNode == null && traversedNodes.empty())) {
            //System.out.println("STEP-00");
            
            // 右端まで行く
            while (currNode != null) {
                // ノードをPUSH
                traversedNodes.push(currNode);
                //System.out.println("PUSH " + currNode.getKey());
                // 右のノードを次のノードとして設定する
                currNode = (MultiKeyTreeNode)currNode.right_;
            }
            
            // 一つ前に戻る
            currNode = (MultiKeyTreeNode)traversedNodes.pop();
            
            /*
             * ここから主処理を書く
             */
             
            // キーを設定
            keys_[layer] = currNode.getKey();
            
            // 最下層でなければ
            if (layer < iBottom_ - 1 ) {
                // ヘッダ処理 A00/02/10
                listener_.makeHeader(currNode.getKey(), layer + 1, currNode.getData());
                // 下位レベルのノードがある
                if (currNode.lowerNode != null) {
                    if (orders_[layer + 1] == ASC) {
                        traverseAsc((MultiKeyTreeNode)currNode.lowerNode, layer + 1);     // 昇順
                    } else {
                        traverseDesc((MultiKeyTreeNode)currNode.lowerNode, layer + 1);    // 降順
                    }
                }
            }
            
            // 最下層の場合
            if (layer == iBottom_ - 1) {
                // 明細レベルの処理
                listener_.sort(keys_, layer + 1, currNode.getData());
            }
            
            // 最下層でなければ
            if (layer < iBottom_ - 1 ) {
                // フッタ処理 A00/02/10
                listener_.makeFooter(currNode.getKey(), layer + 1, currNode.getData());
            }
            
            // 左ノードの設定
            currNode = (MultiKeyTreeNode)currNode.left_;
        }
    }
    
    /**
     *  keysで指定されたノード配下の1階層だけを昇順で走査します。
     *  このメソッドの中から、
     *  {@link OneLayerListener#sortOneLayer(Object, Object)}メソッドが呼び出されます。
     *  @param keys キーの配列
     *  @exception NoSuchElementException 指定したキーを持つノードが見つからなかった場合
     */
    public void traverseOneLayer(Object[] keys) {
        traverseOneLayer(keys, ASC);
    }
    
    /**
     *  keysで指定されたノード配下の1階層だけをorderで指定されたソート順で走査します。
     *  このメソッドの中から、
     *  {@link OneLayerListener#sortOneLayer(Object, Object)}メソッドが呼び出されます。
     *  @param keys キーの配列
     *  @param order ソート順
     *  @exception NoSuchElementException 指定したキーを持つノードが見つからなかった場合
     */
    public void traverseOneLayer(Object[] keys, boolean order) {
        // 指定したキーを持つ開始ノードを検索
        MultiKeyTreeNode startNode = this.find(keys, keys.length);
        
        // 指定したキーを持つノードが見つからなかった場合
        if (startNode == null) {
            throw new NoSuchElementException();
        }
        
        // 下位ノードがある場合
        if (startNode.lowerNode != null) {
            if (order == ASC) {
                this.traverseOneLayerAsc(startNode.lowerNode);    //昇順トラバース
            } else {
                this.traverseOneLayerDesc(startNode.lowerNode);   //降順トラバース
            }
        }
    }
    
    /**
     *  currNodeを起点として1階層分の昇順走査を実行します。
     *  このメソッドの中から、
     *  {@link OneLayerListener#sortOneLayer(Object, Object)}メソッドが呼び出されます。
     *  @param currNode 走査を開始するノード
     */
    private void traverseOneLayerAsc(MultiKeyTreeNode currNode) {
        Stack oneLayerNodes = new Stack<>();    // 当レベルのノードをスタックする
        
        /*** ループ版に変更 M011004 YT0050 ***/
        while (!(currNode == null && oneLayerNodes.empty())) {
            //System.out.println("STEP-00");
            
            // 左端まで行く
            while (currNode != null) {
                // ノードをPUSH
                oneLayerNodes.push(currNode);
                //System.out.println("PUSH " + currNode.getKey());
                // 左のノードを次のノードとして設定する
                currNode = (MultiKeyTreeNode)currNode.left_;
            }
            
            // 一つ前に戻る
            currNode = (MultiKeyTreeNode)oneLayerNodes.pop();
            
            // リスナーアクションの呼び出し
            oneLayerListener_.sortOneLayer(currNode.getKey(), currNode.getData());
            
            // 右ノードの設定
            currNode = (MultiKeyTreeNode)currNode.right_;
        }
    }
    
    /**
     *  currNodeを起点として1階層分の降順走査を実行します。
     *  このメソッドの中から、
     *  {@link OneLayerListener#sortOneLayer(Object, Object)}メソッドが呼び出されます。
     *  @param currNode 走査を開始するノード
     */
    private void traverseOneLayerDesc(MultiKeyTreeNode currNode) {
        Stack oneLayerNodes = new Stack<>();    // 当レベルのノードをスタックする
        
        /*** ループ版に変更 M011004 YT0050 ***/
        while (!(currNode == null && oneLayerNodes.empty())) {
            //System.out.println("STEP-00");
            
            // 右端まで行く
            while (currNode != null) {
                // ノードをPUSH
                oneLayerNodes.push(currNode);
                //System.out.println("PUSH " + currNode.getKey());
                // 右のノードを次のノードとして設定する
                currNode = (MultiKeyTreeNode)currNode.right_;
            }
            
            // 一つ前に戻る
            currNode = (MultiKeyTreeNode)oneLayerNodes.pop();
            
            // リスナーアクションの呼び出し
            oneLayerListener_.sortOneLayer(currNode.getKey(), currNode.getData());
            
            // 左ノードの設定
            currNode = (MultiKeyTreeNode)currNode.left_;
        }
    }
    
    /**
     *  keysで指定されたノード配下の1階層分のキーを昇順にソートし、
     *  その結果をイテレータとして返します。
     *  当メソッドを利用するクラスでは、{@link OneLayerListener}
     *  インタフェースを実装する必要があります。
     *  返すイテレータは木と同期されないので、イテレータ走査中に木が更新されても、
     *  その結果はイテレータには反映されません。
     *  @param keys キーの配列
     *  @return キー集合のイテレータ
     */
    public Iterator keys(Object[] keys) {
        return this.keys(keys, ASC);
    }
    
    /**
     *  keysで指定されたノード配下の1階層分のキーをorderで指定された順にソートし、
     *  その結果をイテレータとして返します。
     *  当メソッドを利用するクラスでは、{@link OneLayerListener}
     *  インタフェースを実装する必要があります。
     *  返すイテレータは木と同期されないので、イテレータ走査中に木が更新されても、
     *  その結果はイテレータには反映されません。
     *  @param keys キーの配列
     *  @return キー集合のイテレータ
     */
    public Iterator keys(Object[] keys, boolean order) {
        // 現在のリスナーを待避する
        OneLayerListener listener = oneLayerListener_;
        myListener_.init();
        this.addOneLayerListener(myListener_);
        
        try {
            this.traverseOneLayer(keys, order);
        } catch (NoSuchElementException e) {
            // 無視する
        } finally {
            this.addOneLayerListener(listener);
        }
        return myListener_.getKeys().iterator();
    }
    
    /**
     *  keysで指定されたノード配下の1階層分のキーを昇順にソートし、
     *  そのキーに対応するデータの並びをイテレータとして返します。
     *  当メソッドを利用するクラスでは、{@link OneLayerListener}
     *  インタフェースを実装する必要があります。
     *  返すイテレータは木と同期されないので、イテレータ走査中に木が更新されても、
     *  その結果はイテレータには反映されません。
     *  @param keys キーの配列
     *  @return 値集合のイテレータ
     */
    public Iterator values(Object[] keys) {
        return this.values(keys, ASC);
    }
    
    /**
     *  keysで指定されたノード配下の1階層分のキーをorderで指定された順にソートし、
     *  そのキーに対応するデータの並びをイテレータとして返します。
     *  当メソッドを利用するクラスでは、{@link OneLayerListener}
     *  インタフェースを実装する必要があります。
     *  返すイテレータは木と同期されないので、イテレータ走査中に木が更新されても、
     *  その結果はイテレータには反映されません。
     *  @param keys キーの配列
     *  @return 値集合のイテレータ
     */
    public Iterator values(Object[] keys, boolean order) {
        // 現在のリスナーを待避する
        OneLayerListener listener = oneLayerListener_;
        myListener_.init();
        this.addOneLayerListener(myListener_);
        
        try {
            this.traverseOneLayer(keys, order);
        } catch (NoSuchElementException e) {
            // 無視する
        } finally {
            this.addOneLayerListener(listener);
        }
        return myListener_.getValues().iterator();
    }
}

// ---- End of class MultiKeyTree

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



Copyright © 2002-2016, Your Technology Partner. All rights reserved.