PriorityQueueの続きのつづき

今日は実装を書いてみます。

  • Priorityインタフェース
package priorityQueue;

/**
 * 優先度を取得するインタフェース。
 * @see PriorityAndTimeComparator
 */
public interface Priority {
    int getPriority();
}

おしまい。
じゃなくて、引き続き、

  • PriorityAndTimeComparatorクラス
package priorityQueue;

import java.util.Comparator;
import java.util.Map;
import java.util.WeakHashMap;


/**
 * 優先度と登録順で優先順を決定するComparatorです。<br>
 * 優先度は以下の順番になります。
 * <ol>
 * <li>Priorityを実装していて、優先度が高いオブジェクト。</li>
 * <li>Priorityを実装していて、優先度が低いオブジェクト。</li>
 * <li>Priorityを実装していないオブジェクト。</li>
 * </ol>
 * 上記の優先度が等しい場合は、登録された順に優先度が高くなります。
 * @see Priority
 */
public class PriorityAndTimeComparator implements Comparator {

    /**
     * カウンターを保持するMap。
     */
    private final Map map =
        new WeakHashMap();
    
    /**
     * カウンター。
     */
    private int counter = 1;

    /**
     * 優先順を計算します。
     */
    public int compare(Object o1, Object o2) {
        // 各オブジェクトのカウンターを取得する。Priorityでない場合も、
        // Queueへの登録順(counter)を計算するため実施する。
        int o1Cnt = counter(o1);
        int o2Cnt = counter(o2);
        
        if (o1 instanceof Priority) {
            // o1とo2がどちらもPriorityの場合はcomparePrioritiesを起動。
            // o1のみがPriorityの場合は-1。
            return o2 instanceof Priority
                    ? comparePriorities((Priority)o1, o1Cnt,
                                        (Priority)o2, o2Cnt)
                    : -1;
        } else {
            // o2のみがPriorityの場合は1、どちらもPriorityではない場合は
            // counterから計算。
            return o2 instanceof Priority ? 1 : o1Cnt - o2Cnt;
        }
    }
    
    /**
     * 2つのPriorityの優先順を比較します。
     */
    private int comparePriorities(Priority p1, int o1Cnt,
                                  Priority p2, int o2Cnt) {
        int sub = p1.getPriority() - p2.getPriority();
        // プライオリティーに差がある場合、その値。差が無い場合、
        // counterから計算。
        return sub != 0 ? sub : o1Cnt - o2Cnt;
    }
    
    /**
     * 渡されたオブジェクトのカウンターを取得します。
     */
    private int counter(Object o) {
        Integer value = map.get(o);
        if (value == null) {
            // カウンターが存在しない場合、生成して登録する
            value =  counter++;
            map.put(o, value);
        }
        return value;
    }
}

クラス名にセンスが感じられない。
#昔からの仕様です。

このクラスはMapのkeyでメモリリークしないようにWeakHashMapを使っています。しかし、Queueに登録する要素にStringを渡した場合、GC対象になってくれません(手元のお手軽テストでは)。Stringって、キャッシュしているんだっけ?なんかそんな話を引いたことがあるような気がするんだけど。そのキャッシュがいつかはGC対象になってくれるのであれば良いが、なってくれないのであればStringのWrapperを作ったほうが良いのかも。
でも、普通Stringなんてつめないか・・・解決しました。

#これとSequenceable...を組み合わせれば・・・面白いものが出来るかなとつぶやいてみる。