ストップウォッチ

/**
 * 時間計測用クラス
 */
public class StopWatch {

  static {
    // JITコンパイルでも走るのか、最初の1回目は遅いので適当に動かしておく
    StopWatch s = new StopWatch(2, TimeUnit.NANOSECONDS);
    s.start();
    s.phase("");
    s.split("");
    s.stop();
    s.phases();
  }

  /**
   * 個々の経過時間を保持するクラス
   */
  public class Phase {

    private String _name;
    private AtomicLong _start;
    private long _end;

    /**
     * 記録を識別する名前を返す
     *
     * @return 名前
     */
    public String getName() {
      return _name;
    }

    /**
     * 経過時間を返す。<br/>
     * 単位はStopWatchクラスのコンストラクタに指定した{@link TimeUnit}による。(デフォルトはナノ秒)
     *
     * @return 時間
     */
    public long getTime() {
      return _timeUnit.convert(_end - _start.get(), TimeUnit.NANOSECONDS);
    }

    /**
     * "名前=時間"形式の文字列を返す
     *
     * @return
     */
    @Override
    public String toString() {
      return String.format("%s=%d", _name, getTime());
    }

  }

  private int _phaseCount = 1; // stopの分
  private TimeUnit _timeUnit = TimeUnit.NANOSECONDS;
  private long _startTime = -1;
  private Phase[] _phases;
  private AtomicInteger _phaseIndex = new AtomicInteger();

  /**
   * デフォルトコンストラクタ。<br/>
   * {@link #phase(String)}、{@link #split(String)}は使用できない。
   */
  public StopWatch() {}

  /**
   * フェーズ計測、スプリット計測をするためのコンストラクタ
   *
   * @param phaseCount
   *            想定される{@link #phase(String)}、{@link #split(String)}の最大呼出し回数。<br/>
   *            この数を超えて呼出すとエラーになる。
   * @param timeUnit
   *            測定結果の単位。内部的にはすべてナノ秒で記録するが、各メソッドの結果はこの単位で丸められる。
   */
  public StopWatch(int phaseCount, TimeUnit timeUnit) {
    _phaseCount = phaseCount + 1;
    _timeUnit = timeUnit;
  }

  /**
   * 計測を開始する
   */
  public void start() {
    _phases = new Phase[_phaseCount];
    // phase()呼出し時に後続のすべてのPhaseの開始時間を再設定していると時間がかかるので、それを1回で済ませるためにこの時点ではすべてのPhaseで開始時間を共有する
    AtomicLong start = new AtomicLong();
    for (int i = 0; i < _phaseCount; i++) {
      _phases[i] = new Phase();
      _phases[i]._start = start;
    }
    _phaseIndex.set(0);
    // 開始時間の取得は極力遅らせる
    start.set(_startTime = System.nanoTime());
  }

  /**
   * {@link #start()}もしくは直前の{@link #phase(String)}呼出しからの経過時間を記録する。<br/>
   * このメソッドのみスレッドセーフ。
   *
   * @param name
   *            記録を識別する名前
   * @return 経過時間
   */
  public long split(String name) {
    long now = System.nanoTime();
    checkRunning();
    Phase phase = _phases[_phaseIndex.getAndIncrement()];
    phase._name = name;
    // 後続のPhaseと開始時間を切り離すために再設定
    phase._start = new AtomicLong(phase._start.get());
    phase._end = now;
    return phase.getTime();
  }

  /**
   * {@link #start()}もしくは直前の{@link #phase(String)}呼出しからの経過時間を記録する
   *
   * @param name
   *            記録を識別する名前
   * @return 経過時間
   */
  public long phase(String name) {
    long now = System.nanoTime();
    checkRunning();
    Phase phase = _phases[_phaseIndex.getAndIncrement()];
    phase._name = name;
    // 後続のPhaseと開始時間を切り離すために再設定
    phase._start = new AtomicLong(phase._start.get());
    phase._end = now;
    long ret = phase.getTime();
    // 後続のPhaseの開始時間を再設定
    // 開始時間の取得は極力遅らせる
    // 後続のPhaseは同じAtomicLongを共有しているので再設定するのは1つだけでOK
    _phases[_phaseIndex.get()]._start.set(System.nanoTime());
    return ret;
  }

  /**
   * 計測を止める
   *
   * @return {@link #start()}からの経過時間
   */
  public long stop() {
    long now = System.nanoTime();
    checkRunning();
    Phase phase = _phases[_phaseIndex.getAndIncrement()];
    phase._name = "stop";
    phase._start = new AtomicLong(_startTime);
    phase._end = now;
    return phase.getTime();
  }

  private void checkRunning() {
    if (_startTime == -1) {
      throw new IllegalStateException();
    }
  }

  /**
   * 記録した結果を配列で返す
   *
   * @return
   */
  public Phase[] phases() {
    return Arrays.copyOf(_phases, _phaseIndex.get());
  }

}

変更の可/不可を切り替えられるコレクション

public class ConditionalModifiableCollection<T> implements Collection<T> {

  private class ConditionalModifiableIterator implements Iterator<T> {

    private Iterator<T> it = baseCollection.iterator();

    @Override
    public boolean hasNext() {
      return it.hasNext();
    }

    @Override
    public T next() {
      return it.next();
    }

    @Override
    public void remove() {
      if (!isModifiable()) {
        throw new IllegalStateException();
      }
      it.remove();
    }
  }

  private Collection<T> baseCollection;
  private boolean modifiable = true;

  public ConditionalModifiableCollection(Collection<T> collection) {
    baseCollection = collection;
  }

  public boolean isModifiable() {
    return modifiable;
  }

  public void setModifiable(boolean value) {
    modifiable = value;
  }

  @Override
  public int size() {
    return baseCollection.size();
  }

  @Override
  public boolean isEmpty() {
    return baseCollection.isEmpty();
  }

  @Override
  public boolean contains(Object o) {
    return baseCollection.contains(o);
  }

  @Override
  public Object[] toArray() {
    return baseCollection.toArray();
  }

  @Override
  public <T> T[] toArray(T[] a) {
    return baseCollection.toArray(a);
  }

  @Override
  public boolean containsAll(Collection<?> c) {
    return baseCollection.containsAll(c);
  }

  @Override
  public Iterator<T> iterator() {
    return new ConditionalModifiableIterator();
  }

  @Override
  public boolean add(T e) {
    if (!isModifiable()) {
      throw new IllegalStateException();
    }
    return baseCollection.add(e);
  }

  @Override
  public boolean remove(Object o) {
    if (!isModifiable()) {
      throw new IllegalStateException();
    }
    return baseCollection.remove(o);
  }

  @Override
  public boolean addAll(Collection<? extends T> c) {
    if (!isModifiable()) {
      throw new IllegalStateException();
    }
    return baseCollection.addAll(c);
  }

  @Override
  public boolean removeAll(Collection<?> c) {
    if (!isModifiable()) {
      throw new IllegalStateException();
    }
    return baseCollection.removeAll(c);
  }

  @Override
  public boolean retainAll(Collection<?> c) {
    if (!isModifiable()) {
      throw new IllegalStateException();
    }
    return baseCollection.retainAll(c);
  }

  @Override
  public void clear() {
    if (!isModifiable()) {
      throw new IllegalStateException();
    }
    baseCollection.clear();
  }

}
public class ConditionalModifiableMap<K, V> implements Map<K, V> {

  private class InnerCollection<T> extends ConditionalModifiableCollection<T> {

    public InnerCollection(Collection<T> set) {
      super(set);
    }

    @Override
    public boolean isModifiable() {
      return ConditionalModifiableMap.this.isModifiable();
    }

    @Override
    public void setModifiable(boolean value) {
      throw new UnsupportedOperationException();
    }

  }

  private class InnerSet<T> extends InnerCollection<T> implements Set<T> {

    public InnerSet(Set<T> set) {
      super(set);
    }

  }

  private class InnerEntrySet extends InnerSet<Map.Entry<K, V>> {

    private class InnerEntry implements Map.Entry<K, V> {

      private Map.Entry<K, V> entry;

      private InnerEntry(Map.Entry<K, V> entry) {
        this.entry = entry;
      }

      @Override
      public K getKey() {
        return entry.getKey();
      }

      @Override
      public V getValue() {
        return entry.getValue();
      }

      @Override
      public V setValue(V value) {
        if (!isModifiable()) {
          throw new IllegalStateException();
        }
        return entry.setValue(value);
      }
    }

    private class InnerEntrySetIterator implements Iterator<Map.Entry<K, V>> {

      private Iterator<Map.Entry<K, V>> iterator;

      private InnerEntrySetIterator(Iterator<Map.Entry<K, V>> iterator) {
        this.iterator = iterator;
      }

      @Override
      public boolean hasNext() {
        return iterator.hasNext();
      }

      @Override
      public Map.Entry<K, V> next() {
        return new InnerEntry(iterator.next());
      }

      @Override
      public void remove() {
        // iteratorはConditionalModifiableIteratorでしかあり得ないが省略するとわかりにくいのでチェックする
        if (!isModifiable()) {
          throw new IllegalStateException();
        }
        iterator.remove();
      }
    }

    private InnerEntrySet(Set<Entry<K, V>> set) {
      super(set);
    }

    @Override
    public Iterator<Map.Entry<K, V>> iterator() {
      return new InnerEntrySetIterator(super.iterator());
    }
  }

  private Map<K, V> baseMap;
  private boolean modifiable  = true;

  public ConditionalModifiableMap(Map<K, V> map) {
    baseMap = map;
  }

  public boolean isModifiable() {
    return modifiable;
  }

  public void setModifiable(boolean value) {
    modifiable = value;
  }

  @Override
  public int size() {
    return baseMap.size();
  }

  @Override
  public boolean isEmpty() {
    return baseMap.isEmpty();
  }

  @Override
  public boolean containsKey(Object key) {
    return baseMap.containsKey(key);
  }

  @Override
  public boolean containsValue(Object value) {
    return baseMap.containsValue(value);
  }

  @Override
  public V get(Object key) {
    return baseMap.get(key);
  }

  @Override
  public V put(K key, V value) {
    if (!isModifiable()) {
      throw new IllegalStateException();
    }
    return baseMap.put(key, value);
  }

  @Override
  public V remove(Object key) {
    if (!isModifiable()) {
      throw new IllegalStateException();
    }
    return baseMap.remove(key);
  }

  @Override
  public void putAll(Map<? extends K, ? extends V> m) {
    if (!isModifiable()) {
      throw new IllegalStateException();
    }
    baseMap.putAll(m);
  }

  @Override
  public void clear() {
    if (!isModifiable()) {
      throw new IllegalStateException();
    }
    baseMap.clear();
  }

  @Override
  public Set<K> keySet() {
    return new InnerSet<>(baseMap.keySet());
  }

  @Override
  public Collection<V> values() {
    return new InnerCollection<>(baseMap.values());
  }

  @Override
  public Set<Map.Entry<K, V>> entrySet() {
    return new InnerEntrySet(baseMap.entrySet());
  }

}

Listもいる?

後始末のできるThreadLocal

/**
 * GCでインスタンスが解放される前に任意の処理を行うためのThreadLocal。<br/>
 * 必要な解放処理は{@link Closeable#close()}に実装する。<br/>
 * 注意点:<br/>
 * <ul>
 * <li>このThreadLocal以外でインスタンスの強い参照を持っていても、スレッドがなくなると{@link Closeable#close()}が呼ばれる。</li>
 * <li>set(A)の後にset(B)とすると、Aの{@link Closeable#close()}が呼ばれる。</li>
 * <li>インスタンスが解放の対象になっても、いずれかのスレッドで{@link #set(T)}/{@link #get()}/{@link #remove()}が呼ばれなければ解放されない。</li>
 * </ul>
 *
 * @param <T> 保持するインスタンスの型
 */
public class CloseableThreadLocal<T extends Closeable> extends ThreadLocal<T> {

  // リフレクションを使って別スレッドのThreadLocal変数をクリアするためのアクセサ
  private static Field THREADLOCALS_FIELD = null;
  private static Method THREAD_LOCAL_MAP_REMOVE_METHOD = null;
  private static boolean REFLECTION_OK = false;

  static {
    try {
      THREADLOCALS_FIELD = Thread.class.getDeclaredField("threadLocals");
      THREADLOCALS_FIELD.setAccessible(true);
      THREAD_LOCAL_MAP_REMOVE_METHOD = THREADLOCALS_FIELD.getType().getDeclaredMethod("remove", ThreadLocal.class);
      THREAD_LOCAL_MAP_REMOVE_METHOD.setAccessible(true);
      REFLECTION_OK = true;
    } catch (Exception e) {
    }
  }

  // GCで解放されたスレッドが参照していたインスタンスが格納されるキュー
  private final ReferenceQueue<Thread> _queue = new ReferenceQueue<>();

  /**
   * スレッドとインスタンスを関連づけるクラス
   */
  private class CloseableHolder extends WeakReference<Thread> {

    private AtomicReference<T> _value;

    public CloseableHolder(T closeable) {
      super(Thread.currentThread(), _queue);
      _value = new AtomicReference<>(closeable);
    }

    public void close() {
      @SuppressWarnings("resource")
      T closeable = _value.getAndSet(null);
      if (closeable != null) {
        try {
          closeable.close();
        } catch (IOException e) {
          throw new RuntimeException(e);
        }
      }
    }

    public T getCloseable() {
      return _value.get();
    }

    public void setCloseable(T closeable) {
      _value.set(closeable);
    }
  }

  @SuppressWarnings("unchecked")
  private void cleanup() {
    CloseableHolder ref = null;
    while ((ref = (CloseableHolder)_queue.poll()) != null) {
      ref.close();
    }
  }

  private final Map<Thread, CloseableHolder> _threads = Collections.synchronizedMap(new WeakHashMap<Thread, CloseableHolder>());

  @Override
  public T get() {
    cleanup();
    T ret = super.get();
    if (ret == null) {
      return null;
    }
    CloseableHolder ref = _threads.get(Thread.currentThread());
    if (ref != null) {
      if (ref.getCloseable() != ret) {
        ref.close();
        ref.setCloseable(ret);
      }
    } else {
      ref = new CloseableHolder(ret);
      _threads.put(Thread.currentThread(), ref);
    }
    return ret;
  }

  @Override
  public void set(T value) {
    cleanup();
    CloseableHolder ref = _threads.get(Thread.currentThread());
    if (ref != null) {
      if (ref.getCloseable() != value) {
        ref.close();
        ref.setCloseable(value);
      }
    } else {
      if (value != null) {
        ref = new CloseableHolder(value);
        _threads.put(Thread.currentThread(), ref);
      }
    }
    super.set(value);
  }

  @Override
  public void remove() {
    cleanup();
    CloseableHolder ref = _threads.get(Thread.currentThread());
    if (ref != null) {
      ref.close();
    }
    super.remove();
  }

  /**
   * 保持しているすべてのインスタンスを解放する
   */
  public void clear() {
    // ThreadLocalMapにアクセスできない場合はそっちと矛盾が出るのでエラーとする
    if (!REFLECTION_OK) {
      throw new IllegalStateException("ThreadLocalMap reflection access failed.");
    }
    List<Thread> keys = new ArrayList<>(_threads.keySet());
    for (Thread t : keys) {
      CloseableHolder ref = _threads.remove(t);
      if (ref == null) {
        continue;
      }
      ref.close();
      forceRemove(t);
    }
  }

  private void forceRemove(Thread thread) {
    try {
      Object threadLocals = THREADLOCALS_FIELD.get(thread);
      if (threadLocals != null) {
        THREAD_LOCAL_MAP_REMOVE_METHOD.invoke(threadLocals, this);
      }
    } catch (Exception e) {
    }
  }

}

java8でgrep

どこかのページでこんな課題を見かけたので作ってみる。
単純な文字列検索で可、
行番号も出力する、
forやwhile、ifなどの制御構文は使用不可という前提で。

String[] args = new String[] {"stream", "pair"};
Stream<Integer> count = Stream.iterate(1, i -> i + 1);
Stream<String> lines = Files.lines(Paths.get("Hoge.java"));
Stream<Pair<Integer, String>> pairs = zip(count, lines);
pairs.filter(pair -> Arrays.stream(args).anyMatch(keyword -> pair.right.indexOf(keyword) >= 0)).forEach(System.out::println);

Pairクラスとzipメソッドはこちらを参考にさせていただきました。


出力する時のフォーマットはPairクラスのtoString()にそれっぽく書くということで。

java8での日時書式化

java8で日時を書式化してみた。

// 一般的?
print("LocalDateTime");
print(LocalDateTime.now()); // 2014-03-28T01:09:49.059
print(LocalDateTime.now().format(DateTimeFormatter.ofPattern("Gyyyy/MM/dd HH:mm:ss"))); // 西暦2014/03/28 01:09:49

// タイムゾーンを意識しなければこれらは使わない?
print("OffsetDateTime");
print(OffsetDateTime.now()); // 2014-03-28T01:09:49.115+09:00
print(OffsetDateTime.now().format(DateTimeFormatter.ofPattern("Gyyyy/MM/dd HH:mm:ss"))); // 西暦2014/03/28 01:09:49
print("ZonedDateTime");
print(ZonedDateTime.now()); // 2014-03-28T01:09:49.116+09:00[Asia/Tokyo]
print(ZonedDateTime.now().format(DateTimeFormatter.ofPattern("Gyyyy/MM/dd HH:mm:ss"))); // 西暦2014/03/28 01:09:49

和暦を使う場合はこうする。

print("JapaneseDate");
print(JapaneseDate.now()); // Japanese Heisei 26-03-28
print(JapaneseDate.now().format(DateTimeFormatter.ofPattern("Gyy年MM月dd日"))); // 平成26年03月28日

JapaneseDateは名前の通り日付しか持たない。
時間を持たせる時はこうする。

print("和暦と時間");
print(JapaneseDate.now().atTime(LocalTime.now())); // Japanese Heisei 26-03-28T01:09:49.122
print(JapaneseDate.now().atTime(LocalTime.now()).format(DateTimeFormatter.ofPattern("Gyy年MM月dd日 HH時mm分ss秒"))); // 平成26年03月28日 01時09分49秒

ちょっと疑問を感じたのでテスト

// 擬似的にatTime()の外と中で日付をまたぐ
// GMTの15時=JSTの真夜中0時
print("和暦と時間。日付またぎテスト");
print(JapaneseDate.now(Clock.fixed(Instant.parse("2014-03-27T14:59:59.999Z"), ZoneId.systemDefault())).atTime(LocalTime.now(Clock.fixed(Instant.parse("2014-03-27T15:00:00.000Z"), ZoneId.systemDefault())))); // Japanese Heisei 26-03-27T00:00
print(JapaneseDate.now(Clock.fixed(Instant.parse("2014-03-27T14:59:59.999Z"), ZoneId.systemDefault())).atTime(LocalTime.now(Clock.fixed(Instant.parse("2014-03-27T15:00:00.000Z"), ZoneId.systemDefault()))).format(DateTimeFormatter.ofPattern("Gyy年MM月dd日 HH時mm分ss秒"))); // 平成26年03月27日 00時00分00秒

案の定3/27の0時になってしまった。

正しく取得するにはこうすることになるのかな?

print("和暦と時間その2");
Clock now = Clock.fixed(Instant.parse("2014-03-27T14:59:59.999Z")/*実際にはInstant.now()にする*/, ZoneId.systemDefault());
// Clock.now()としてはいけない
// Clock.now()で作成したインスタンスは作成した時の時間を保持せず、必要に応じてシステム日時を取得しなおしている
print(JapaneseDate.now(now).atTime(LocalTime.now(now))); // Japanese Heisei 26-03-27T23:59:59.999
print(JapaneseDate.now(now).atTime(LocalTime.now(now)).format(DateTimeFormatter.ofPattern("Gyy年MM月dd日 HH時mm分ss秒"))); // 平成26年03月27日 23時59分59秒

2014/3/28追記
こっちの方が簡単かな?

print(LocalDateTime.now().format(DateTimeFormatter.ofPattern("Gy").withChronology(JapaneseChronology.INSTANCE)))

だとするとJapaneseDateの使い道がわからない・・・