Android エミュレータの hosts ファイルを編集
Android エミュレータの hosts ファイルを編集する方法です。
以下のような方法が考えられます。
- (a) shell で hosts へ行を追加
- (b) pull と push で hosts を変更
(a) shell で hosts へ行を追加
行を追加するだけなら以下のように shell を使うだけです。
adb remount
しておかないと /system/etc/hosts ファイルを更新できずに Read-only file system
のようなエラーメッセージが表示される点に注意。 (既に adb remount 済みの場合は不要)
hosts への行追加
> adb remount remount succeeded > adb shell "echo 10.0.2.2 server1 >> /system/etc/hosts"
/system/etc/hosts の内容を確認してみます。
hosts の内容確認
> adb shell cat /system/etc/hosts 127.0.0.1 localhost 10.0.2.2 server1
10.0.2.2 はエミュレータから接続する際のホスト OS の IP アドレスなので、ping してみます。
ping の実行
> adb shell ping server1 PING server1 (10.0.2.2) 56(84) bytes of data. 64 bytes from server1 (10.0.2.2): icmp_seq=1 ttl=255 time=0.000 ms ・・・
remount に関して
adb remount
する事で以下のように /system が rw で再マウントされます。
adb remount 前 (ro)
/dev/block/mtdblock0 /system ext4 ro,relatime,data=ordered 0 0
adb remount 後 (rw)
/dev/block/mtdblock0 /system ext4 rw,relatime,data=ordered 0 0
(b) pull と push で hosts を変更
次は hosts ファイルを編集する方法です。
エミュレータには vi 等のエディタが用意されていないようなので、pull で hosts ファイルを取得して、ホスト OS 側で hosts ファイルを編集し、push でエミュレータへ反映します。
まずは adb pull
で hosts ファイルを取得します。
hosts を pull
> adb pull /system/etc/hosts
ホスト OS のカレントディレクトリへ pull された hosts ファイルを編集します。
hosts 変更例
127.0.0.1 localhost 10.0.2.2 server1
変更した hosts を push します。
まだ一度も remount していない場合は remount する必要があります。
hosts を push
> adb remount ・・・ > adb push hosts /system/etc/
Swift のビルド環境を Docker で構築
Ubuntu をベースとした Swift のビルド環境を Docker で構築してみました。
使用したソースは http://github.com/fits/try_samples/tree/master/blog/20151207/
Docker イメージ作成
ubuntu:15.10
のイメージをベースに Swift をセットアップしました。
clang 等の必要なパッケージをインストールした後、swift-2.2-SNAPSHOT-2015-12-01-b-ubuntu15.10.tar.gz をダウンロードして解凍し、環境変数 PATH へ swift-2.2-・・・/usr/bin
のパスを追加するだけです。
また、clang
をインストールしただけだと swiftc の実行時に /usr/bin/ld: warning: libicuuc.so.55, needed by /swift-2.2・・・/usr/lib/swift/linux/libswiftCore.so, not found (try using -rpath or -rpath-link)
のようなメッセージが出力されたので、libicu-dev
もインストールするようにしています。
Dockerfile
FROM ubuntu:15.10 ENV SWIFT_PACKAGE swift-2.2-SNAPSHOT-2015-12-01-b-ubuntu15.10 RUN apt-get update RUN apt-get -y upgrade RUN apt-get -y install curl clang libicu-dev # swift のパッケージをダウンロード RUN curl https://swift.org/builds/ubuntu1510/swift-2.2-SNAPSHOT-2015-12-01-b/$SWIFT_PACKAGE.tar.gz -o $SWIFT_PACKAGE.tar.gz RUN tar zxf $SWIFT_PACKAGE.tar.gz RUN rm -f $SWIFT_PACKAGE.tar.gz RUN apt-get clean # 環境変数 PATH の設定 ENV PATH /$SWIFT_PACKAGE/usr/bin:$PATH
docker build
で Docker イメージを作成します。
Docker イメージの作成
$ docker build --no-cache -t sample/swift:0.1 . ・・・ Successfully built ・・・
動作確認
作成した Docker イメージを使って以下のようなサンプルソースを実行してみます。
/vagrant/work/sample.swift
let a = 11 let b = 2 print("result = \(a * b)")
まず、sample/swift:0.1
イメージを使って Docker コンテナを起動します。
Docker コンテナの起動
$ docker run --rm -it -v /vagrant/work:/work sample/swift:0.1 bash ・・・ # cd /work
コンテナ内で sample.swift を実行します。
直接実行
# swift sample.swift result = 22
ビルドしてから実行
# swiftc sample.swift # ./sample result = 22
Objective-C のビルド環境を Docker で構築
CentOS をベースとした Objective-C のビルド環境を Docker で構築してみました。
使用したソースは http://github.com/fits/try_samples/tree/master/blog/20151130/
Docker イメージ作成
今回は clang と GNUstep を使う事にします。
バージョンは古くなってしまいますが、どちらも yum でインストールするようにしました。
make
をインストールしておかないと GNUstep の gnustep-config
コマンドが機能しない (結果が空になる) のでご注意ください。
また、gcc-objc
をインストールしておかないと、Objective-C のソースをビルドする際に必要なヘッダーファイルを参照できず、fatal error: 'inttypes.h' file not found
のようなエラーが発生しました。
Dockerfile
FROM centos RUN yum -y update RUN yum -y install epel-release RUN yum -y install clang make gcc-objc gnustep-base-devel RUN yum clean all
docker build
で Docker イメージを作成します。
Docker イメージの作成
$ docker build --no-cache -t sample/objc:0.1 . ・・・ Successfully built ・・・
動作確認
以下のようなサンプルソースをビルドしてみます。
/vagrant/work/Sample.h
#import <Foundation/NSObject.h> #import <Foundation/NSString.h> @interface Sample : NSObject { NSString* _name; } @property (nonatomic, copy) NSString* name; - (void)log; @end
/vagrant/work/Sample.m
#import <Foundation/Foundation.h> #import "Sample.h" @implementation Sample : NSObject @synthesize name = _name; - (void)log { NSLog(@"%@", _name); } - (void)dealloc { [_name release]; [super dealloc]; } @end int main(int argc, const char * argv[]) { @autoreleasepool { Sample* s = [[Sample new] autorelease]; s.name = @"test"; [s log]; } return 0; }
まずは sample/objc:0.1
イメージを使って Docker コンテナを起動します。
Docker コンテナの起動
$ docker run --rm -it -v /vagrant/work:/work sample/objc:0.1 bash
コンテナ内で clang を使ってビルドします。
コンパイラオプションに関しては以下で妥当なのかは不明ですが、一応ビルドには成功しました。 (場合によっては -objcmt-migrate-all
等の指定も必要かもしれません)
ビルド
# cd work # clang `gnustep-config --objc-flags` `gnustep-config --objc-libs` -lgnustep-base -I /usr/lib/gcc/x86_64-redhat-linux/4.8.3/include Sample.m -o Sample clang: warning: argument unused during compilation: '-shared-libgcc'
一応、それぞれのオプションを指定しなかった場合に発生したエラーをまとめておきます。
オプション指定 | 指定しなかった場合のエラー例 |
---|---|
`gnustep-config --objc-flags` | 無し(指定しなくてもビルドは成功) |
`gnustep-config --objc-libs` | undefined reference to symbol 'objc_getProperty' |
-lgnustep-base | undefined reference to `NSLog' |
-I /usr/lib/gcc/・・・/include | fatal error: 'objc/objc.h' file not found |
ビルド結果の Sample
を実行してみると一応動作しました。
実行例
# ./Sample 2015-11-30 20:15:00.121 Sample[32] test
なお、clang -v
の実行結果は以下のようになりました。
clang -v
# clang -v clang version 3.4.2 (tags/RELEASE_34/dot2-final) Target: x86_64-redhat-linux-gnu Thread model: posix Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-redhat-linux/4.8.2 Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-redhat-linux/4.8.3 Found candidate GCC installation: /usr/lib/gcc/x86_64-redhat-linux/4.8.2 Found candidate GCC installation: /usr/lib/gcc/x86_64-redhat-linux/4.8.3 Selected GCC installation: /usr/bin/../lib/gcc/x86_64-redhat-linux/4.8.3
twemproxy + Redis 環境を Docker で構築
twemproxy(別名 nutcracker) + Redis の環境を Docker で構築してみます。 (twemproxy は memcached・Redis 用の軽量なプロキシです)
使用した設定ファイル等は http://github.com/fits/try_samples/tree/master/blog/20151124/
Redis の Docker イメージを取得
Redis の Docker イメージは docker pull で取得する事にします。
$ docker pull redis
twemproxy の Docker イメージを構築
twemproxy の Docker イメージは、下記 Dockerfile を使って centos のイメージをベースに twemproxy のソースをビルドして作成する事にします。
Dockerfile
FROM centos RUN yum -y update RUN yum -y install make automake libtool git RUN git clone https://github.com/twitter/twemproxy.git RUN cd twemproxy && autoreconf -fvi && ./configure && make && make install RUN rm -fr twemproxy RUN yum clean all
上記 Dockerfile で docker build を実行すれば Docker イメージを作成できます。
Docker イメージの作成
$ docker build --no-cache -t sample/twemproxy:0.1 . ・・・ Successfully built ・・・
twemproxy のビルドで warning は出ましたが、Docker イメージの作成に成功しました。
twemproxy + Redis × 2 の実行
twemproxy と Redis 2台を個別の Docker コンテナで実行してみます。
twemproxy の設定ファイルで接続する Redis サーバーを <ホスト名 or IPアドレス>:<ポート番号>:<重み>
で指定する必要があり、これが課題となります。
twemproxy 設定ファイル例
sample: ・・・ redis: true servers: - 127.0.0.1:6380:1 - 127.0.0.1:6381:1 ・・・
今回は、起動スクリプトを使って twemproxy と Redis の Docker コンテナをまとめて起動するようにしてみました。
該当コンテナを docker start (コンテナを再起動) してみて、失敗した場合は docker run を行うようにしています。 (docker start し易いように --name=<コンテナ名>
で名前を付けています)
また、コンテナ間の接続を容易にするため、docker run 時に -h <ホスト名>
でホスト名も付けました。 (コンテナ名をそのままホスト名に使っています)
/vagrant/tmp/start_twemproxy (twemproxy + Redis コンテナの起動スクリプト)
#!/bin/sh INDENT=" " CONF_DIR=/vagrant/tmp/conf CONTAINER_CONF_DIR=/conf TWEMPROXY_IMAGE=sample/twemproxy:0.1 CONTAINER_REDIS_LIST="redis1 redis2" CONTAINER_TWEMPROXY=twemproxy1 REDIS_SERVERS="" # Redis のコンテナを個々に起動 for s in $CONTAINER_REDIS_LIST do if test -z `docker start $s`; then docker run --name=$s -h $s -d redis else echo start $s fi REDIS_SERVERS="$REDIS_SERVERS$INDENT- $s:6379:1\n" done # twemproxy の設定ファイルを生成 sed -e "s/#{SERVERS}/$REDIS_SERVERS/g" `dirname $0`/nutcracker.yml.tpl > $CONF_DIR/nutcracker.yml # twemproxy のコンテナ起動 if test -z `docker start $CONTAINER_TWEMPROXY`; then docker run --name=$CONTAINER_TWEMPROXY -h $CONTAINER_TWEMPROXY -d -p 6379:6379 -v $CONF_DIR:$CONTAINER_CONF_DIR $TWEMPROXY_IMAGE nutcracker -c $CONTAINER_CONF_DIR/nutcracker.yml else echo start $CONTAINER_TWEMPROXY fi
twemproxy は nutcracker -c <設定ファイル>
で起動しています。
また、twemproxy 設定ファイル(nutcracker.yml)は Redis サーバーの構成に合わせて下記テンプレートの #{SERVERS}
を sed
で置き換えて生成するようにしています。
/vagrant/tmp/nutcracker.yml.tpl (twemproxy 設定ファイルのテンプレート)
sample: listen: 0.0.0.0:6379 hash: fnv1a_64 distribution: ketama timeout: 500 redis: true servers: #{SERVERS}
実行
それでは実行してみます。
初回起動時は docker start に失敗し docker run を実施する事になります。
twemproxy1, redis1, redis2 コンテナの実行例
$ /vagrant/tmp/start_twemproxy Error response from daemon: no such id: redis1 Error: failed to start containers: [redis1] bdc12db5・・・ Error response from daemon: no such id: redis2 Error: failed to start containers: [redis2] fd4546fc・・・ Error response from daemon: no such id: twemproxy1 Error: failed to start containers: [twemproxy1] 87bb567e・・・
redis-cli で twemproxy1 へ接続する Docker コンテナを起動し (Redis の Docker イメージを使っています) 、動作確認してみます。
動作確認
$ docker run --rm -it redis redis-cli -h twemproxy1 twemproxy1:6379> set a 123 OK twemproxy1:6379> get a "123"
twemproxy は正常に動作しているようです。
redis1・2 へ直接接続するコンテナをそれぞれ実行し確認すると、値は redis2 の方に設定されていました。
$ docker run --rm -it redis redis-cli -h redis1 redis1:6379> keys * (empty list or set) redis1:6379> exit $ docker run --rm -it redis redis-cli -h redis2 redis2:6379> keys * 1) "a" redis2:6379> exit
備考 - Vagrant 利用時
Vagrant で起動したゲスト OS 上で Docker を実行している場合、Vagrantfile へ以下のような設定を追加すれば provision で各コンテナを起動しホスト OS から twemproxy へ接続できるようになります。
Vagrantfile
・・・ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| ・・・ # twemproxy 用のポートフォワード設定 config.vm.network "forwarded_port", guest: 6379, host: 6379 # twemproxy + redis の起動スクリプト実行 config.vm.provision :shell, :inline => "/vagrant/tmp/start_twemproxy" end
provision 実施例
> vagrant up --provision ・・・ ==> default: Running provisioner: shell... default: Running: inline script ==> default: start redis1 ==> default: start redis2 ==> default: start twemproxy1
ホスト OS からの接続例
> redis-cli 127.0.0.1:6379> get a "123"
リストをN個に分割 - Groovy, Java, Python
リストをなるべく均等に N 分割する処理を Groovy・Java・Python で実装してみました。
今回は、[0, 1, 2, 3, 4, 5, 6, 7]
を 3分割した結果が [[0, 1, 2], [3, 4, 5], [6, 7]]
となるような処理を想定しています。 (余り分を先頭から順に1つずつ分配)
ソースは http://github.com/fits/try_samples/tree/master/blog/20151109/
Groovy の場合
- Groovy 2.4.5
畳み込みを使って実装してみました。
- (1) サブリストとして取り出す範囲を算出
- (2) 取り出したサブリストを結果へ追加
divide_list.groovy
def divideList = { xs, n -> int q = xs.size() / n int m = xs.size() % n (0..<n).inject([]) { acc, i -> // (1) サブリスト化する範囲の開始・終了位置を算出 def fr = acc*.size().sum(0) def to = fr + q + ((i < m)? 1: 0) // (2) サブリストを取り出し、結果へ追加 acc << xs[fr..<to] } } println divideList(0..<8, 3) println divideList(0..<7, 3) println divideList(0..<6, 3) println divideList(0..<6, 6)
実行結果
> groovy divide_list.groovy [[0, 1, 2], [3, 4, 5], [6, 7]] [[0, 1, 2], [3, 4], [5, 6]] [[0, 1], [2, 3], [4, 5]] [[0], [1], [2], [3], [4], [5]]
なお、上記の実装内容では divideList(0..<3, 5)
のように要素数よりも分割数を多くすると [[0], [1], [2], [], []]
のように不足分が空リストとなります。
場合によっては、以下のように小さい数に合わせた方が実用的かもしれません。
def divideList = { xs, n -> ・・・ nn = Math.min(n, xs.size()) (0..<nn).inject([]) { acc, i -> ・・・ } }
Java 8 の場合
- Java SE 8u66
Groovy 版と同様に実装しました。
Stream
の collect
メソッドを使っています。
DivideList.java
import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; import java.util.stream.IntStream; public class DivideList { public static void main(String... args) { System.out.println(divideList(range(0, 8), 3)); System.out.println(divideList(range(0, 7), 3)); System.out.println(divideList(range(0, 6), 3)); System.out.println(divideList(range(0, 6), 6)); } private static <T> List<List<T>> divideList(List<T> xs, int n) { int q = xs.size() / n; int m = xs.size() % n; return IntStream.range(0, n).collect( ArrayList::new, (acc, i) -> { int fr = acc.stream().mapToInt(List::size).sum(); int to = fr + q + ((i < m)? 1: 0); acc.add(xs.subList(fr, to)); }, ArrayList::addAll ); } private static List<Integer> range(int start, int end) { return IntStream.range(start, end).boxed().collect(Collectors.toList()); } }
実行結果
> java DivideList [[0, 1, 2], [3, 4, 5], [6, 7]] [[0, 1, 2], [3, 4], [5, 6]] [[0, 1], [2, 3], [4, 5]] [[0], [1], [2], [3], [4], [5]]
Python の場合
- Python 3.5.0
Python も同様に実装しました。
変数 fr に値を保持させるため、ラムダを入れ子にして引数のデフォルト値を利用しています。
また、以下のように /
で割ると結果が実数になって都合が悪いため、//
を使っています。
式 | 結果 |
---|---|
5 / 2 | 2.5 |
5 // 2 | 2 |
divide_list.py
from functools import reduce def divide_list(xs, n): q = len(xs) // n m = len(xs) % n return reduce( lambda acc, i: (lambda fr = sum([ len(x) for x in acc ]): acc + [ xs[fr:(fr + q + (1 if i < m else 0))] ] )() , range(n), [] ) range_list = lambda n: list(range(n)) print(divide_list(range_list(8), 3)) print(divide_list(range_list(7), 3)) print(divide_list(range_list(6), 3)) print(divide_list(range_list(6), 6))
実行結果
> python divide_list.py [[0, 1, 2], [3, 4, 5], [6, 7]] [[0, 1, 2], [3, 4], [5, 6]] [[0, 1], [2, 3], [4, 5]] [[0], [1], [2], [3], [4], [5]]
Python の機械学習環境を Docker イメージで作成
書籍「データサイエンティスト養成読本 機械学習入門編 (Software Design plus)」を参考に、numpy・scipy・matplotlib・scikit-learn パッケージをインストールした Python 3.5.0 の環境を Docker イメージとして作成してみました。
サンプルソースは http://github.com/fits/try_samples/tree/master/blog/20151029/
python イメージをベースに作成
まずは、Docker の最新 python イメージ (この時は Python 3.5.0) をベースに Docker イメージを作成します。
なお、書籍では libblas-dev
をインストールしていましたが、ここでは代わりに libatlas-base-dev
をインストールしています。
Dockerfile
FROM python RUN apt-get update && apt-get upgrade -y RUN apt-get install -y libfreetype6-dev libatlas-base-dev liblapack-dev gfortran RUN pip install numpy RUN pip install scipy RUN pip install matplotlib RUN pip install scikit-learn RUN apt-get clean
docker build
で上記の Dockerfile からイメージを作成します。
Docker イメージの作成
$ docker build --no-cache -t sample/python-ml:0.1 .
動作確認
scikit-learn を使った単純な Python スクリプト (SVM で "あやめ" の品種を予測) を実行してみます。
/vagrant/svm_sample.py
from sklearn import datasets from sklearn import svm iris = datasets.load_iris() svc = svm.SVC() svc.fit(iris.data, iris.target) print(svc.predict([[5, 3, 4, 1], [6, 3, 5, 2]]))
先程作成した Docker イメージを docker run
で実行します。
DeprecationWarning が出力されますが、動作しているようです。
実行結果
$ docker run --rm -it -v /vagrant:/work sample/python-ml:0.1 python /work/svm_sample.py ・・・ [1 2]
DeprecationWarning の内容は inspect.getargspec() is deprecated, use inspect.signature() instead
です。
centos イメージをベースに作成
次に、CentOS をベースに Python 3.5.0 をソースからビルドした Docker イメージを作成してみました。
atlas を使う場合、そのままでは libcblas.so
が作られず scikit-learn のビルドに失敗するようなので、libtatlas.so
のシンボリックリンクとして libcblas.so
を作成するようにしています。
pip はデフォルトで pip3.5
としてインストールされるため、今回は pip3.5 をそのまま使って numpy 等をインストールしています。
なお、python
コマンドでは Python 2.7.5 が起動するので、Python 3.5 は python3
コマンドで起動します。
Dockerfile
FROM centos RUN yum update -y && yum install -y make automake libtool openssl-devel curl RUN curl -O https://www.python.org/ftp/python/3.5.0/Python-3.5.0.tgz RUN tar zxf Python-3.5.0.tgz RUN cd Python-3.5.0 && ./configure && make && make install RUN rm -fr Python-3.5.0 && rm -f Python-3.5.0.tgz RUN yum install -y lapack-devel atlas-devel gcc-c++ freetype-devel libpng-devel RUN cd /usr/lib64/atlas && ln -s libtatlas.so libcblas.so RUN pip3.5 install numpy RUN pip3.5 install scipy RUN pip3.5 install matplotlib RUN pip3.5 install scikit-learn RUN yum clean all
Docker イメージの作成
$ docker build --no-cache -t sample/python-ml-centos:0.1 .
動作確認
docker run
で実行すると、先程と同じ結果になりました。
実行結果
$ docker run --rm -it -v /vagrant:/work sample/python-ml-centos:0.1 python3 /work/svm_sample.py ・・・ [1 2]
ResultSet の Stream 化
java.sql.ResultSet
を java.util.stream.Stream
化する方法はいくつか考えられますが、今回は以下の方法を試してみました。
- Spliterator インターフェースの実装クラスを作成
サンプルソースは http://github.com/fits/try_samples/tree/master/blog/20151026/
Spliterator インターフェースを直接実装
java.util.Spliterator
インターフェースを直接実装する場合、以下の 4つのメソッドを実装します。
メソッド名 | 内容 | 備考 |
---|---|---|
tryAdvance | 要素を個々にトラバースする | 要素が存在する場合に引数の Consumer をその要素で実行し true を返す |
estimateSize | 残りの要素数の推定値を返す | 不明な場合は Long.MAX_VALUE を返す |
characteristics | 構造や要素の特性を返す | 複数の特性値を | で連結できる |
trySplit | 要素の一部を分割できる場合に分割する | 分割できない場合は null を返す |
今回は tryAdvance
メソッドの引数 Consumer へ ResultSet を加工した値を渡すように、SQLException を伴う処理を TryFunction インターフェースとして扱っています。
characteristics
では要素の順序が定義されている事を示す ORDERED
のみを返すようにしていますが、(今回のように加工しないで) ResultSet をそのまま使うのなら NONNULL
も追加できると思います。
なお、Stream を返す stream
メソッドは、あると便利なので定義しているだけです。 (無くても問題ありません)
src/main/java/sample/ResultSetSpliterator.java
package sample; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Spliterator; import java.util.function.Consumer; import java.util.stream.Stream; import java.util.stream.StreamSupport; public class ResultSetSpliterator<T> implements Spliterator<T> { private ResultSet resultSet; // ResultSet を加工する処理 private TryFunction<ResultSet, T, SQLException> converter; public ResultSetSpliterator(ResultSet resultSet, TryFunction<ResultSet, T, SQLException> converter) { this.resultSet = resultSet; this.converter = converter; } @Override public boolean tryAdvance(Consumer<? super T> consumer) { try { if (resultSet.next()) { consumer.accept(converter.apply(resultSet)); return true; } } catch (SQLException e) { throw new RuntimeException(e); } return false; } @Override public Spliterator<T> trySplit() { return null; } @Override public long estimateSize() { return Long.MAX_VALUE; } @Override public int characteristics() { return ORDERED; } // Stream 化して返す public Stream<T> stream() { return StreamSupport.stream(this, false); } }
TryFunction の内容は次の通りです。
src/main/java/sample/TryFunction.java
package sample; @FunctionalInterface public interface TryFunction<T, R, E extends Exception> { R apply(T t) throws E; static <T, E extends Exception> TryFunction<T, T, E> identity() { return r -> r; } }
動作確認
動作確認のための実行クラスを用意します。
src/main/java/sample/SampleApp.java
package sample; import java.sql.*; public class SampleApp { private static final String SQL = "select * from product"; public static void main(String... args) throws Exception { try (Connection con = DriverManager.getConnection(args[0])) { sample1(con); System.out.println("---"); sample2(con); } } private static void sample1(Connection con) throws SQLException { try ( PreparedStatement ps = con.prepareStatement(SQL); ResultSet rs = ps.executeQuery() ) { new ResultSetSpliterator<>( rs, r -> r.getString("name") ).stream().forEach(System.out::println); } } private static void sample2(Connection con) throws SQLException { try ( PreparedStatement ps = con.prepareStatement(SQL); ResultSet rs = ps.executeQuery() ) { long count = new ResultSetSpliterator<>( rs, TryFunction.identity() ).stream().count(); System.out.println("count : " + count); } } }
Gradle による実行結果は以下の通り。 一応、Stream として処理できているようです。
実行結果
> gradle run -Pargs="jdbc:mysql://localhost/sample?user=root" ・・・ sample1 sample2 sample3 --- count : 3
Gradle のビルド定義には以下を使いました。
build.gradle
apply plugin: 'application' mainClassName = 'sample.SampleApp' repositories { jcenter() } dependencies { runtime 'mysql:mysql-connector-java:5.1.36' } run { if (project.hasProperty('args')) { args project.args } }
Spliterators.AbstractSpliterator をサブクラス化
直接 Spliterator インターフェースを実装するよりも、Spliterators.AbstractSpliterator
のサブクラスとした方が、少しだけコードを減らせます。
この場合、estimateSize・characteristics の値は super コンストラクタで指定します。
src/main/java/sample/ResultSetSpliterator2.java
package sample; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Spliterators; import java.util.function.Consumer; import java.util.stream.Stream; import java.util.stream.StreamSupport; public class ResultSetSpliterator2<T> extends Spliterators.AbstractSpliterator<T> { private ResultSet resultSet; private TryFunction<ResultSet, T, SQLException> converter; public ResultSetSpliterator2(ResultSet resultSet, TryFunction<ResultSet, T, SQLException> converter) { // estimateSize・characteristics の値を指定 super(Long.MAX_VALUE, ORDERED); this.resultSet = resultSet; this.converter = converter; } @Override public boolean tryAdvance(Consumer<? super T> consumer) { try { if (resultSet.next()) { consumer.accept(converter.apply(resultSet)); return true; } } catch (SQLException e) { throw new RuntimeException(e); } return false; } public Stream<T> stream() { return StreamSupport.stream(this, false); } }