Jython を学ぶ(4)───Jython で Java の配列を使う(2)、 Jython で Java の I/O 関連 API を使う(1)
Jython を学ぶ(4) part1───Jython で Java の配列を使う(2)
前回の続きです。
例を再掲します。
#!jython import jarray buffer=jarray.zeros(3, "b") for i in range(0, len(buffer)): buffer[i]=i for b in buffer: print b buffer1=jarray.zeros(5, "b") from java.lang import System System.arraycopy(buffer, 0, buffer1, 0, 3) print buffer1 pyList=[5, 4, 3, 2, 1] buffer2=jarray.array(pyList, "b") print buffer2
この Jython コードにおおよそ相当する Java のコードは以下の通りです。
class ArrayTest{ public static void main(String args[]){ byte[] buffer=new byte[3]; for(byte i=0; i<buffer.length; i++){ buffer[i]=i; } for(byte b:buffer){ System.out.println(b); } byte[] buffer1=new byte[5]; System.arraycopy(buffer, 0, buffer1, 0, 3); for(byte b:buffer1){ System.out.print(b); } System.out.print("\n"); byte[] buffer2={5, 4, 3, 2, 1}; for(byte b:buffer2){ System.out.print(b); } System.out.print("\n"); } }
javac ArrayTest.java
java ArrayTest
この例の要点は以下の通りです。
jarray.zeros 関数
zeros 関数は第一引数に長さ、第二引数に配列に格納される値型をあらわす文字または格納される参照の型をとり、そのような配列をゼロフィルして返します。
値型を表す文字は Python 標準ライブラリの array モジュールで使われている文字と同じで、以下の通りです。
文字 | 型 |
---|---|
"z" | boolean |
"c" | char |
"b" | byte |
"h" | short |
"i" | int |
"l" | long |
"f" | float |
"d" | double |
jarray.array 関数
array 関数は第一引数に Python のシーケンス、第二引数に zeros と同様の型の指定をとり、シーケンスの内容が格納されたシーケンスと同じ長さの配列を返します。
java.lang.System#arraycopy メソッド
System#arraycopy メソッドは配列を効率的にコピーする静的メソッドです。
http://java.sun.com/javase/ja/6/docs/ja/api/java/lang/System.html#arraycopy(java.lang.Object, int, java.lang.Object, int, int)
arraycopy は Java の for 文でコピーする場合はもちろん、 JNI を使ってコピーするよりはるかに高速です。これは arraycopy が JVM に組み込まれているからです。
arraycopy を使えば、( Java プラットフォームが正しく実装されていれば)型安全に高速な配列のコピーができます。
Jython を学ぶ(4) part2───Jython で Java の I/O 関連 API を使う(1)
Java の配列を扱えるので、バイト配列を多用する Java の I/O を Jython だけでも効率よく使うことができます。
本当は Java7 で導入されるNIO2(JSR 203: More New I/O APIs for the JavaTM Platform ("NIO.2"), http://www.jcp.org/en/jsr/detail?id=203)を使いたいのですが、 Java7 のリリース予定は2009年の夏です。
正式リリース以前のライブラリを使ったコードは例として良くないので、Early Access 版は使いません。
NIO2 では OS の非同期 I/O 機能を使う API が提供されるようです。この機能を使えば OS によって最適化された効率の良い非同期 I/O が実現できるでしょう。
しかし今はまだこれを使えませんので、非常に単純な非同期 I/O 機能を Jython で書くことにします。
以下はカレントディレクトリの source という名前のファイルの内容を destination という名前のファイルにコピーする Jython スクリプトです。
ファイルが存在しなかったりすると例外を送出して停止します。
#!jython import jarray from java.lang import Runnable from java.lang import ThreadLocal from java.io import FileInputStream from java.io import FileOutputStream from java.util.concurrent import Callable from java.util.concurrent import Executors from java.util.concurrent import FutureTask class PyCallableRunnable(Runnable): def __init__(self, pyCallable): self.pyCallable=pyCallable return def run(self): self.pyCallable() return class PyCallableCallable(Callable): def __init__(self, pyCallable): self.pyCallable=pyCallable return def call(self): return self.pyCallable() asyncTranslateThreadLocal=ThreadLocal() asyncTranslateExecutorService=Executors.newSingleThreadExecutor() #newCachedThreadPool(), newFixedThreadPool(n), ... def asyncTranslate(inputStream, outputStream): def translate(): amountTranslated=0 buffer=asyncTranslateThreadLocal.get() if buffer is None: buffer=jarray.zeros(4048, "b") asyncTranslateThreadLocal.set(buffer) while True: amountRead=inputStream.read(buffer) if amountRead==-1: break amountTranslated+=amountRead outputStream.write(buffer, 0, amountRead) return amountTranslated futureTask=FutureTask(PyCallableCallable(translate)) asyncTranslateExecutorService.execute(futureTask) return futureTask def asyncCopy(sourcePath, destinationPath): inputStream=FileInputStream(sourcePath) outputStream=FileOutputStream(destinationPath) future=asyncTranslate(inputStream, outputStream) return future if __name__=="__main__": future=asyncCopy("source", "destination") print future.get() asyncTranslateExecutorService.shutdown()
この場合は非同期である意味がほとんどありませんが、複雑なプログラムでは非同期 I/O が役に立つでしょう。
内容の解説は次回に回します。