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 をインストールしておかないと GNUstepgnustep-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・JavaPython で実装してみました。

今回は、[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 の場合

Groovy 版と同様に実装しました。 Streamcollect メソッドを使っています。

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 も同様に実装しました。

変数 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.ResultSetjava.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);
    }
}