Oracle Databaseのインストーラの文字化け対策

Linux版のOracle Database 11g R2のインストールで文字化けに苦しんだので対策方法をメモ。

文字化けの根本原因はOracle Databaseに付属のJREインストーラを起動するからであって、付属のJRE以外を使うというのが基本的な回避方法。

文字化けしたのは下記の3つ。
・OUI (Oracle Universal Installer) → Oracle Database本体のインストーラ
・DBCA (Database Configuration Assistant) → データベース作成ツール
・NETCA (Net Configuration Assistant) → リスナ作成ツール

Open JDKでもOracleJDKでも良いので付属のJRE以外が必要なので別途ダウンロードしてインストール後、それぞれ文字化けするツールを別途インストールしたJDKに付いてくるJREを見るように設定変更する。

JDKのインストール

Open JDKでもOracleJDKでも良いのでインストールする。
今回はOracleJDKRPM形式でダウンロードしてインストールした。
ダウンロードしたファイルは jdk-8u91-linux-x64.rpm で、それを下記のrpmコマンドでインストールする。

rpm -ivh jdk-8u91-linux-x64.rpm

すると /usr/java/jdk1.8.0_91 にインストールされるので、ここにある jre ディレクトリのJREを以降では使うようにする。

OUI (Oracle Universal Installer) の起動

OUIの起動は runInstaller にパラメータで JRE の場所を指定する。

./runInstaller -jreLoc /usr/java/jdk1.8.0_91/jre/

DBCA (Database Configuration Assistant) の起動

DBCAの起動は、起動シェルを書き換えてJREの場所を指定する。
起動シェルは $ORACLE_HOME/bin/dbca 。

vi $ORACLE_HOME/bin/dbca

"JRE_DIR"で検索すると以下の行がある。

JRE_DIR=/opt/app/oracle/product/11.2.0/db_1/jdk/jre

それを、インストールしたJDKJREを見るように変更する。

#JRE_DIR=/opt/app/oracle/product/11.2.0/db_1/jdk/jre
JRE_DIR=/usr/java/jdk1.8.0_91/jre

あとは通常通りに dbca を起動すればOK。

NETCA (Net Configuration Assistant) の起動

NETCAもDBCAと方法は同じ。
起動シェルを書き換えてJREの場所を指定する。
起動シェルは $ORACLE_HOME/bin/netca 。

vi $ORACLE_HOME/bin/netca

"JRE_DIR"で検索すると以下の行がある。

JRE_DIR=/opt/app/oracle/product/11.2.0/db_1/jdk/jre

それを、インストールしたJDKJREを見るように変更する。

#JRE_DIR=/opt/app/oracle/product/11.2.0/db_1/jdk/jre
JRE_DIR=/usr/java/jdk1.8.0_91/jre

あとは通常通りに netca を起動すればOK。

マスターテーブルとenum

昨日、三浦カズヒトさんと Twitter でやりとりさせてもらった。

あまり深く考えずにリプライしてしまい中途半端にしか答えられなかったので、ツイッターの文字数制限に縛られない場所で自分の考えをまとめてみた。

問題

データベースのマスターテーブルにキーと名称のペアがあったとして、それと同じペアをenumや定数でプログラムに持つのが、果たして良いことなのか。
どうせマスターにペアを追加するときはそれに対応するプログラムをデプロイするし、マスターとプログラムの両方を手直しするならマスターなんて持たずにプログラムだけで対応すれば良いのではないだろうか。
マスターに名称を持つ価値は、せいぜい急な名称変更に対応出来る程度なのではないか。

マスターとenumの両方を持つ理由

マスターには値の名称を持ち、enumではそのマスターに応じた処理を分岐させるための定数とするよう、両方を持つのだと思う。
例えば、マスターに以下のようなものがあったとする。

種別番号 種別名
0
1
2

それに応じたenumを用意すると、こうなる。

public enum Kind {
    DAI,    // 種別番号 0「大」 を表す
    CYU,    // 種別番号 1「中」 を表す
    SYO     // 種別番号 2「小」 を表す
}

で、種別ごとに何らかの処理を実装すると、こうなる。

    public void someProcess(Kind kind) {
        switch (kind) {
            case DAI:
                // 種別番号 0「大」 の時の処理
                break;

            case CYU:
                // 種別番号 1「中」 の時の処理
                break;

            case SYO:
                // 種別番号 2「小」 の時の処理
                break;
        }
    }

このような実装をしていたら、マスターに種別を増やした場合にそれに応じたenumを増やして、更にそのenumを使っている箇所に処理を追加しないとならない。
例えば「極小」を追加したらマスターは下記のようになる。

種別番号 種別名
0
1
2
3 極小

それに応じてenumの「GOKUSYO」も増やす。

public enum Kind {
    DAI,    // 種別番号 0「大」 を表す
    CYU,    // 種別番号 1「中」 を表す
    SYO,    // 種別番号 2「小」 を表す
    GOKUSYO // 種別番号 3「極小」 を表す
}

さらに「case GOKUSYO」の処理を増やす。

    public void someProcess(Kind kind) {
        switch (kind) {
            case DAI:
                // 種別番号 0「大」 の時の処理
                break;

            case CYU:
                // 種別番号 1「中」 の時の処理
                break;

            case SYO:
                // 種別番号 2「小」 の時の処理
                break;

            case GOKUSYO:
                // 種別番号 3「極小」 の時の処理
                break;
        }
    }

種別が変更になるたびにマスターとenumと処理を増やすのは、正直しんどい。
だったらマスターを捨ててenumだけで良いのでは?というのも分かる。

マスターだけにする

種別に応じた処理をマスターに基いてやるようにすれば、enumも処理も不要になるケースがある。
例えばマスターに処理をするのに必要なパラメータを登録するというやり方。

種別番号 種別名 パラーメータ1 パラーメータ2
0 789 JKL
1 456 GHI
2 123 DEF
3 極小 0 ABC

処理はこのようになる。

    public void someProcess(int kind) {
        int param1 = getParam1(kind);    // マスターから種別番号に応じたパラメータ1を取得
        String param2 = getParam2(kind); // マスターから種別番号に応じたパラメータ2を取得

        // param1とparam2を基に処理する

    }

処理は同じだが種別によってパラメータが違うという場合なら、これで十分対応が可能と思う。
これならマスターだけをメンテすれば万事OK。

マスターもenumも持たない

マスターがキーと値のペアだけだったら、マスターを持たないという選択もありだと思う。
かと言って、enumと処理がバラバラだとそれはそれでしんどいので、もうマスターもenumも持たないようにしたい。

マスターをなくしたので、種別に応じた種別名を取得する仕組みを作る。
ひとつの答えはインターフェースかと思う。

public interface Kind {
    public String getName();   // 種別名を返す
}

で、種別に応じた実装クラスを用意する。

public class Kind0 implements Kind {
    public String getName() {
        return "大";
    }
}

public class Kind1 implements Kind {
    public String getName() {
        return "中";
    }
}

public class Kind2 implements Kind {
    public String getName() {
        return "小";
    }
}

public class Kind3 implements Kind {
    public String getName() {
        return "極小";
    }
}

これで元々マスターにあった「キーに応じた値」を得ることは出来る。

せっかくインターフェースにしたなら、冒頭にあった

    public void someProcess(Kind kind) {
        switch (kind) {
            case DAI:
                // 種別番号 0「大」 の時の処理
                break;

            case CYU:
                // 種別番号 1「中」 の時の処理
                break;

            case SYO:
                // 種別番号 2「小」 の時の処理
                break;
        }
    }

この種別が増えるたびに分岐が増える汚コードもすっきりさせられる。

インターフェースに、この時にするべき処理を定義する。

public interface Kind {
    public String getName();   // 種別名を返す
    public void someProcess(); // するべき処理
}

で、種別に応じた「するべき処理」を実装する。

public class Kind0 implements Kind {
    public String getName() {
        return "大";
    }
    public void someProcess() {
        // "大"の時にするべき処理
    }
}

public class Kind1 implements Kind {
    public String getName() {
        return "中";
    }
    public void someProcess() {
        // "中"の時にするべき処理
    }
}

public class Kind2 implements Kind {
    public String getName() {
        return "小";
    }
    public void someProcess() {
        // "小"の時にするべき処理
    }
}

public class Kind3 implements Kind {
    public String getName() {
        return "極小";
    }
    public void someProcess() {
        // "極小"の時にするべき処理
    }
}

そうすれば、汚コードもすっきりする。

    public void someProcess(Kind kind) {
        kind.someProcess();
    }

呼び出し元はインターフェースしか見えないので実際に何が実行されるかは、渡ってきたKindの実装クラスに応じて変わってくる。
変数kindはKind1のインスタンス参照かもしれないし、Kind2のインスタンス参照かもしれない。

実装はすっきりしたので、あとはインスタンスの生成だが、ここは局所化すれば汚コードでも良いと思う。

public class KindFactory {
    public static Kind create(int id) {
        switch (id) {
            case 0:
                return new Kind0();
            case 1:
                return new Kind1();
            case 2:
                return new Kind2();
            case 3:
                return new Kind3();
        }
        throw new IllegalArgumentException(id+"って何よ?");
    }
}

マジックナンバーが嫌だというなら、内部的なenumを使うのも良いかもしれない。

public class KindFactory {
    private enum K {
        DAI,    // 種別番号 0「大」 を表す
        CYU,    // 種別番号 1「中」 を表す
        SYO,    // 種別番号 2「小」 を表す
        GOKUSYO // 種別番号 3「極小」 を表す
    }

    public static Kind create(int id) {
        if (K.DAI.ordinal()==id) {
            return new Kind0();

        } else if (K.CYU.ordinal()==id) {
            return new Kind1();

        } else if (K.SYO.ordinal()==id) {
            return new Kind2();

        } else if (K.GOKUSYO.ordinal()==id) {
            return new Kind3();
        }
        throw new IllegalArgumentException(id+"って何よ?");
    }
}

いやいやそれでも条件分岐が多くて汚コードは嫌だと言うなら、idに応じたクラスをプロパティファイルに書いて、リフレクションでインスタンスを作ると条件分岐がなくて尚良しかと。

まとめ

そうは言っても状況によって求められることは違うので、ここで書いたことが絶対に正しいとも思わないし、プロダクトによって答えは変わってくるかと。

今更聞けない基礎英語

中学レベルに立ち返って基礎英語をまとめている。
このエントリーを随時更新する予定。

be動詞、一般動詞

be動詞

「~である」「~です」のように、主語と動詞以降の文がイコールであることを表現するのに用いるのがbe動詞。

■語順:

主語 + be動詞

■例:

・I am a travel journalist.
 → 私は旅行記者です

・You are a student.
 → あなたは生徒です

・He is a boss.
 → 彼はボスです


be動詞には以下の8種類がある。

be動詞
be
am
are
is
was
were
been
being

be動詞というだけあって、beが原形で、主語や時間軸などによって使い分ける。
be been being was were は一回忘れて、それ以外の am are is は主語によって下記のように使い分ける。

主語 be動詞
I am
you are
その他 is

■例:
・I am a travel journalist.
 → 私は旅行記者です

・You are a student.
 → あなたは生徒です

・He is a boss.
 → 彼はボスです


一般動詞

be動詞以外の、動作を表現するのに用いるのが一般動詞。

■語順:

主語 + 一般動詞

■例:

・I study English.
 → 私は英語を勉強する

・You speak English.
 → あなたは英語を話す

・He plays basketball.
 → 彼はバスケットボールをする


疑問文、否定文

be動詞の疑問文、否定文

be動詞を用いた疑問文はbe動詞を前に持ってくる

■語順:

be動詞 + 主語 + ・・・?

■例:

Are you a student?
 → あなたは生徒ですか?

be動詞を用いた否定文はbe動詞の後ろにnotを入れる

■語順:

主語 + be動詞 + not

■例:

・I am not a travel journalist.
 → 私は旅行記者ではありません

一般動詞の疑問文、否定文

一般動詞を用いた疑問文はdoを先頭に付ける

■例:

Do you speak English?
 → あなたは英語を話せますか?

一般動詞を用いた否定文はdo notを一般動詞の前に付ける

■例:

・You do not speak English?
 → あなたは英語を話せません


疑問詞

疑問詞には以下の8種類がある。

疑問詞 意味
how どのように
what
when いつ
where どこ
which どちら
who 誰が
whose 誰の
why なぜ

疑問詞は文の先頭に使用する。

■語順:

疑問詞 + ・・・

■例:

How to study English?
 → どのように英語を勉強する?

What is this?
 → これはですか?

When to arrive?
 → いつ到着しますか?

Where is this?
 → ここはどこですか?

Which is your pen?
 → どちらがあなたのペンですか?

Who is this?
 → これはですか?

Whose is this?
 → これは誰のですか?

Why Japanese people?
 → なぜなのだ日本人?


命令文

命令文には「~しなさい」と強い言い方から「~してください」や「~しましょう」なども含まれる。
共通して言えるのは「主語がない」ということ。
また命令文の否定形もある。

命令文「~しなさい」

主語を取り除き、動詞の原形が先頭に来る。

■語順:

動詞の原形 + ・・・

■例:

Study English.
 → 英語を勉強しなさい

be動詞の場合は、be動詞の原形のbeになる。

■例:

Be a good student.
 → 良い生徒になりなさい

否定命令文「~するな」

Do notを先頭につけて、Do not + 動詞の原形にする。

■語順:

Do not + 動詞の原形

■例:

Do not study English.
 → 英語を勉強するな

be動詞の場合でも、Do not を先頭につける。

■例:

Do not be a good student.
 → 良い生徒になるな

命令文(依頼)「~てください」

Pleaseを先頭につけて、Please + 動詞の原形にする。

■語順:

Please + 動詞の原形

■例:

Please study English.
 → 英語を勉強してください

命令文(勧誘)「~しましょう」

Let'sを先頭につけて、Let's + 動詞の原形にする。

■語順:

Let's + 動詞の原形

■例:

Let's study English.
 → 英語を勉強しましょう

三人称単数現在

一般動詞は、主語が三人称で、単数で、現在文の時だけsがつく。
(いわゆる「三単現のs」)
代名詞だと、He や She や It で、一般の名詞だと代名詞にした時に He や She や It になるものが対象。

■例:

・He plays basketball.
 → 彼はバスケットボールをする。

・My sister studes English.
 → 私の妹は英語を勉強する。
  (My sister は代名詞にすると She になる)

・Mr.Foo speaks English.
 → フーさんは英語を話す。
  (Mr.Foo は代名詞にすると He もしくは She になる)

以下の例は三人称単数現在ではないのでsはつかない。

■例:

I play basketball.
 → 私はバスケットボールをする。
  (一人称)

You play basketball.
 → あなたはバスケットボールをする。
  (二人称)

We play basketball.
 → 私達はバスケットボールをする。
  (複数

They play basketball.
 → 彼らはバスケットボールをする。
  (複数

・He played basketball.
 → 彼はバスケットボールをした。
  (過去)

・He will play basketball.
 → 彼はバスケットボールをする。
  (未来)

sのつけかた

一般動詞の最後が s sh ch x o の場合は es をつける。

■例:

・bless
 → blesses

・wash
 → washes

・watch
 → watches

・fix
 → fixes

・go
 → goes

子音 + yで終わっている場合 y を i にかえて es をつける。

■例:

・study
 → studies

・country
 → countries

その他は単純に s をつける。

■例:

・play
 → plays

・speak
 → speaks

例外で、have は has になる。

■例:

・have
 → has

現在進行形

現在行っていることを表す。
主語 + be動詞 + 動詞のing形
  ※be動詞が入るのに注意

■例:

・I am studying English.
 → 私は英語を勉強している

・You are speaking English.
 → あなたは英語を話している

・He is playing basketball.
 → 彼はバスケットボールをしている

動詞のing形

動詞に ing をつければ良い。

■例:

・study
 → studying

・speak
 → speaking

・play
 → playing

最後が e で終わる動詞は、その e を取って ing をつける。

■例:

・have
 → having

・use
 → using

・make
 → making

短子音 + 子音字で終わる動詞は、子音字を重ねる。

■例:

・run
 → running

・swim
 → swimming

・stop
 → stopping

・cut
 → cutting

疑問文

be動詞を前にする。

■語順:

be動詞 + 主語 + 動詞のing形 + ・・・?

■例:

Am I studying English?
 → 私は英語を勉強していますか?

Are you speaking English?
 → あなたは英語を話していますか?

Is he playing basketball?
 → 彼はバスケットボールをしていますか?

否定文

be動詞の後ろに not を入れる。

■語順:

主語 + be動詞 + not + 動詞のing形

■例:

・I am not studying English.
 → 私は英語を勉強していません

・You are not speaking English.
 → あなたは英語を話していません

・He is not playing basketball.
 → 彼はバスケットボールをしていません


助動詞

主語と動詞の間に置き、動詞の意味を助ける働きをする。

■語順:

主語 + 助動詞 + 動詞の原形

助動詞には以下のようなものがある。

助動詞 意味
can ~できる
may ~かもしれない
will ~する(未来形)
must ~するべき
■例:

・You can speak English.
 → あなたは英語を話すことができる

・You may speak English.
 → あなたは英語を話すかもしれない

・You will speak English.
 → あなたは英語を(将来的に)話す。

・You must speak English.
 → あなたは英語を話すべき

疑問文

助動詞を先頭に持ってくる。

■語順:

助動詞 + 主語 + 動詞の原形 + ・・・?

■例:

Can you speak English?
 → あなたは英語を話すことができますか?

May you speak English.
 → あなたは英語を話すかもしれなですか?

Will you speak English?
 → あなたは英語を(将来的に)話しますか?。

Must you speak English.
 → あなたは英語を話すべきですか?

否定文

助動詞の後ろに not を入れる。

■語順:

主語 + 助動詞 + not + 動詞の原形

■例:

・You can not speak English.
 → あなたは英語を話すことができない

・You may not speak English.
 → あなたは英語を話さないかもしれない

・You will not speak English.
 → あなたは英語を(将来的に)話さない

・You must not speak English.
 → あなたは英語を話すべきではない

SQLiteのジャーナルモード

2つのSQLiteのデータベースファイルがあり、それぞれ更新すると一方は「-journal」ファイルが作成されて、もう一方は「-shm」と「-wal」ファイルが作成されるという違うファイルが作成される理由がわからず調べたら、ジャーナルモードが違ったというお話。

ジャーナルモードの大分

SQLiteのジャーナルモードは大きく分けて「delete」と「wal」がある。
「delete」の時は「-journal」ファイルが作成され、「wal」の時は「-shm」と「-wal」ファイルが作成される。
例えば「foo」というデータベースファイルがあると、ジャーナルモードが「delete」の時は「foo」というファイルの他に「foo-journal」というファイルが作成され、ジャーナルモードが「wal」の時は「foo-shm」と「foo-wal」というファイルが作成される。
どちらもジャーナルファイルなので、本体のデータベースにコミットするまでのデータが書き込まれるという意味では同じだが、動作は若干異なる。

ジャーナルモードの詳細は本家のサイトを参照。
http://www.sqlite.org/pragma.html#pragma_journal_mode

deleteモード

トランザクション開始から終了までの更新内容を「-journal」ファイルに書き込み、コミットしたときに本体のデータベースファイルへ更新内容を反映し、反映したら「-journal」ファイルを削除する。
最後の「-journal」ファイルを削除する動作に因んで「deleteモード」と呼んでいる。
コミットした時点でデータベースファイルを更新するため、大きなデータベースファイルの場合は書き込みに時間がかかるかもしれない。(未検証)

walモード

トランザクション開始から終了までの更新内容を「-shm」ファイルに書き込み、コミットした時に「-wal」ファイルへ更新内容を書き込む。
コミットした時点では本体データベースファイルには更新内容を書き込まないため、本体に比べて小さいファイルの更新でトランザクションを完了できるメリットがある。
なのでデータ更新が多いシステムではwalモードの方が有利と言える。(未検証)
ただしデータが本体データベースファイルとwalファイルに分散されるため、データの取得には時間がかかるかもしれない。(未検証)

walファイルはデータベース接続を閉じる時に、その内容を本体データベースファイルに反映する。
つまりクローズ処理はdeleteモードに比べて時間がかかる。

永続性の違い

先の通り、deleteモードはコミットした時点で本体データベースに更新を反映する一方、walモードではコミットしても本体データベースに更新を反映しない。
そのため、もしコミット後にシステムが異常終了等した場合、walモードでは本体データベースに更新を反映するためのリカバリが必要になる。

その他のジャーナルモード

上の2つ以外にも以下のようなジャーナルモードがある。

truncateモード

deleteモードの亜種で、コミットしても「-journal」ファイルを削除せず、0バイトのファイルを残しておく。
ファイル作成時の権限等のリスクを減らすには有利かもしれない。
データベースを閉じて再度接続するとdeleteモードになる。

persistモード

deleteモードの亜種で、コミットしても「-journal」ファイルの内容を削除せずに取っておく。
データベースを閉じて再度接続するとdeleteモードになる。

memoryモード

ジャーナルをファイルではなくメモリで管理する。
とにかく動作を速くしたい場合は選択する価値があるかもしれないが、メモリの容量に注意が必要かもしれない。
データベースを閉じて再度接続するとdeleteモードになる。

offモード

ジャーナリングしない・・・ということか?(未検証)

まとめ

deleteモードかwalモードはそれぞれメリット・デメリットがあるので、システムの特性に合わせて適切に使い分けましょう。

DAG (Directed acyclic graph) - 有向非巡回グラフ

先日、JJUG CCC 2015 Fallに参加して、灰色のサイトで有名なひしだまさん(ひしだま (@hishidama) | Twitter)の「GH-6 Java8 Stream APIとApache SparkとAsakusa Frameworkの類似点・相違点」を聞いた際に出てきた「DAG (Directed acyclic graph) - 有向非巡回グラフ」が面白そうだったので調べてみた。

DAGとは

Wikiによると、

グラフ理論における閉路のない有向グラフの事。
有向グラフは頂点と有向辺(方向を示す矢印付きの辺)からなり、辺は頂点同士をつなぐが、ある頂点 v から出発し、辺をたどり、頂点 v に戻ってこないのが有向非巡回グラフである。

wikipedia:有向非巡回グラフ

となっている。

DAGの例

Wikiの例はわかりづらいので、JJUG CCC 2015 Fallのひしだまさんのスライドを見ると、18枚目に例が載っている。

左は向きがない(無向)なのでDAGではない。
中央は菱型の条件分岐で上に戻っている(巡回)のでDAGではない。
右は向きがあって(有向)条件分岐があるが循環していない(非巡回)のでDAGであると言える。

ひしだまさんの発表は、DAGで例題を示し、Stream APIとSparkとAsakusaFWの相違点について説明するという流れになっていた。
説明も聞いていたが、自分はこのDAGが気になってしまって、心の半分がDAGに持って行かれていたことは内緒としておきたい・・・。

DAGの活用

DAGを使うと、今まで言葉でしか説明していなかったものや、なんとなく図式化していたものを明確に説明できるのではないだろうか。

例えば

社員一覧画面は、社員マスタからデータを取得し表示する。
ただし所属は所属マスタから取得し表示すること。

のようなものは下記のように表せる。
f:id:nave_kazu:20151130154420p:plain

うん。わかりやすい。


もう少し複雑な例だと日締め処理のバッチなど。

その日一日の取引内容を集計する。
集計結果はPDF化し担当部署長宛にメールにて通知する。

のようなものは下記のように表せる。
f:id:nave_kazu:20151130154422p:plain

ここまでやってて、もっとDAGを用いるのに適した分野があることに気付いた。
そう、料理のレシピだ。

DAGを用いた料理のレシピ

例えばみんなのきょうの料理に掲載されている「かじきのガーリック炒めレシピ」だと、このようになる。
f:id:nave_kazu:20151130154424p:plain

ちなみにカレーのレシピはこのようになる。
f:id:nave_kazu:20151130154427p:plain

このように、DAGの応用範囲は広いということがわかった。
料理のレシピはDAGでも併せて記述するべきだと思う。

JavaのStream API(IntStream)を触ってみた

調子に乗ってIntStreamも使ってみた。

UNIX/Linux系アプリにありがちな、実行時引数に「-」を使って色々なパラメータを渡すアレを受け取る処理を書いてみた。

例題として以下のパラメータを使用する。

  • -c → 設定ファイルの指定
  • -i → 入力ファイルの指定
  • -o → 出力ファイルの指定

例えば以下のようにパラメータを指定する。
FooApplication -c configFile -i inputFile -o outputFile

当然、順番を変えて以下のように指定しても同じ意味となる。
FooApplication -i inputFile -c configFile -o outputFile

Javaの場合、mainメソッドの引数「String[] args」にそれぞれの値が配列に格納されて渡ってくるので、今回はそれをIntStreamを使って処理をする。
処理結果はMapに詰めて、キーにスイッチの値( -c など)を、バリューにそのスイッチの値 (configFile)を詰めることにする。

コードで書いた処理結果のイメージは以下の通り。

    Map<String, String> map = new HashMap<>();

    map.put("-c", "configFile");
    map.put("-i", "inputFile");
    map.put("-o", "outputFile");

Stream APIを使わなかった場合は以下の通り。(諸々のエラー処理は省略)

    Map<String, String> map = new HashMap<>();

    for (int i=0; i<args.length; i++) {
        if (args[i].startsWith("-")) {
            map.put(args[i], args[i+1]);
        }
    }

これがStream APIを使うと以下の通り。(諸々のエラー処理は省略)

    Map<String, String> map = new HashMap<>();

    IntStream.range(0, args.length)
        .filter(i -> args[i].startsWith("-"))
        .forEach(i -> map.put(args[i], args[i+1]));

・・・たぶん、IntStreamはこういう使い方をするためのものではないよね。
うん。知ってて使ってみた。