Java
streem

Javaでstreemを実装して感じたこと

More than 3 years have passed since last update.

日経Linuxで連載されている記事とMatzさんの実装を基にJavaでstreemを実装してみて、ハマったポイントなどを書きます。

はじまり

昨年(2014年)末頃に日経Linuxの連載記事を知ってから、そのうち読んでみようと思っていたのですが今夏まで放置。YAPC::Asia Tokyo 2015でMatzさんのトークを聞いて記事を読む。そして、実装してみたくなり以下の様に考えて即日作成開始。
- Cでそのまま写経すると飽きてすぐやめてしまいそうなのでC以外で書く
- VM実装は大変そうなので既存VM上で動くものにしたい
- 知っている言語の中からマルチスレッドの実装がしやすそうなJavaを選択

Javaを書くのは初めてですが、これを機にスキルアップできればと思います。

構文解析器は問題なく完成

構文解析器はJavaCCを使って生成しました。
これに関しては特にハマる所もなくスムーズに作れました。
YAPC::Asiaの2日目の夜に早速取り掛かり、翌日会場内で完成という感じ。

コア部分の実装開始

日経Linux 2015年3月号相当のものの実装は結構苦労しました。
使ったクラスは以下。
- スレッドは単純にThreadクラス
- I/O多重化はepoll相当(?)のSelectorクラス

たぶん普通ですね。
しかし、Javaもマルチスレッドプログラミングも初めてだった事もあり、デバッグに
めちゃくちゃ時間が掛かりました。
とりあえず、標準入力からの入力文字列を全て大文字に変換して標準出力に出力するところまでできるようになっています。
streem的に書くとこんな感じでしょうか。

stdin | to_upper_case() | stdout

ハマったこと、感じたこと

  1. 標準入出力を単純にSelectorに登録できない
  2. Selectorを使うにはjava.nio.Bufferのサブクラスまでセットで使う必要がある
  3. 普通にcharとbyteの変換でハマる

まず1についてですが、Selectorに登録できるI/OチャネルはAbstractSelectableChannelのサブクラスのみで、このサブクラスの中に標準入出力に相当するチャネルが見当たらなかった。その為、java.nio.channels.Pipeクラスを使って、Pipe.SinkChannelと標準入力を繋ぎPipe.SourceChannelをSelectorに登録して代替としました。そして、標準入力の読み込みでブロックしない様、この部分だけの為に別スレッドを起動しています。

2については、java.nio.ByteBufferやjava.nio.CharBufferを使うことになり、バッファ操作に関して慣れるまでOverFlowを起こしまくりました。

3は最も基本的な事だと思いますが、Javaのcharがutf-16で格納されるのでutf-8のテキスト入力をbyteで受け取って単純にそのままcharに変換したら文字化けという事です。utf-8のテキストを受け取ったByteBufferを一発でCharBufferに変換できないので、ByteBuffer->byte[]->String(utf-8指定)->char[]->CharBufferという変換を毎回やることに。

今後も引き続き...

たぶん続けます。
単純に面白いし、Javaやマルチスレッドプログラミングのスキルアップに繋がると思うので。

https://github.com/koji-m/JetStreem

...ところで

全然先の話になると思いますが、JVM上で動的型付け言語を実装するにはどの様にすれば良いのだろうかと考えていて、JRubyを参考にするにもコード読むの大変そうだしという事で以下参考になりそうなJVM言語を発見。
Golo -a lightweight dynamic language for the JVM.

あと、コアの実装はこのライブラリを使うともっと簡単に実装できるかも、、とか。
Project Reactor -Reactive FastData for a New Age