JDK 24 で追加された java.io.Reader.of()

blog1.mammb.com


はじめに

CharSequence は文字の並びを表すインターフェースで、その実装には String に加え StringBuffer, StringBuilder, CharBuffer などがあります。

CharSequence から Reader を得るには、以下のように一度文字列に変換する必要がありました。

var r = new StringReader(cs.toString());

なお、Commons IO が使える環境であれば、以下で済みます。

var r = new org.apache.commons.io.input.CharSequenceReader(cs);


java.io.Reader.of()

JDK 24 では、Reader に以下のスタティックメソッドが追加され、CharSequence から Reader を取得できるようになりました。

public static Reader of(final CharSequence cs) {
    // ...
}

このメソッドでは、引数の CharSequence をラップした Reader の匿名クラスを返すため、toString() による無駄なインスタンスの生成を抑制できます。

文字の読み込みは、以下のように CharSequence の実装により切り替える実装になっています。

public static Reader of(final CharSequence cs) {

    return new Reader() {
        // ...
        @Override
        public int read(char[] cbuf, int off, int len) throws IOException {
            // ...
            switch (cs) {
                case String s -> s.getChars(next, next + n, cbuf, off);
                case StringBuilder sb -> sb.getChars(next, next + n, cbuf, off);
                case StringBuffer sb -> sb.getChars(next, next + n, cbuf, off);
                case CharBuffer cb -> cb.get(next, cbuf, off, n);
                default -> {
                    for (int i = 0; i < n; i++)
                        cbuf[off + i] = cs.charAt(next + i);
                }
            }
            next += n;
            return n;
        }
    }

CharSequence を実装した未知のクラスに対しては、char 単位でのループ処理にフォールバックされるので、パフォーマンスを重視する場面では注意する必要があります。

また、Reader.of() で取得した Reader は同期処理が省かれているため、マルチスレッド環境で利用する場合は自身で同期処理を行うか、旧来通り StringReader を利用します(こちらは同期処理が含まれています)。


JDK11 で追加された nullReader() と合わせて、Reader には2つのスタティックファクトリメソッドが存在することになります。

public abstract class Reader implements Readable, Closeable {
  public static Reader nullReader() { ... }
  public static Reader of(final CharSequence cs) { ... }
}

nullReader() は、読み込みに対して常にストリームの終端に達しているような結果を返し、close() 後は IOException をスローします。 ちなみに nullReader() に応当する nullWriter() も JDK11 で追加されており、こちらは書き込まれた全ての文字を単に破棄します。いずれもテストなどの状況で利用することを想定したものになります。

public abstract class Writer implements Appendable, Closeable, Flushable {
  public static Writer nullWriter() { }
}


まとめ

あえて書くほどでもない小さな話ではありますが、 JDK 24 で java.io.Reader.of(CharSequence) が追加されました。

このスタティックファクトリメソッドを使うことで CharSequence からの Reader 生成パフォーマンスの改善が期待できます。

生成した Reader に複数スレッドからアクセスする場合は、自身で同期処理を行うか、旧来通り StringReader を利用してください。