Java」カテゴリーアーカイブ

(今更)double braceでのCollection初期化

以前より取り沙汰されてはいる、Java-Collectionのdouble-braceによる初期化について。

Map<String, String> mp = new HashMap<String, String>() {{
    put("abc", "ABC");
    put("def", "DEF");
   put("ghi", "GHI");
}};

のようにインスタンス生成と初期化を行うコードはJava8以前にはちょいちょい見られ。
今もまぁちょいちょい使うのだけれど。
このdouble-braceによる初期化当初よりメモリリークが指摘され、注意が必要とされている。
匿名内部クラス(annonymous inner class)が生成され”強い外部参照”が作成されるためガーベージコレクションされることなく残り続けるというもので、繰り返し処理されるロジックでは避けるべきとされている。
(https://blog.p-y.wtf/avoid-java-double-brace-initialization で詳しく解説されている)

でもって、じゃぁ、というのはJava9以降なら簡単で、

Map<String, String> mp = Map.of("abc", "ABC", "def", "DEF", "ghi", "GHI"};

とするだけ。もちろんMap.ofの場合はImmutable Collection(不変コレクション)を返すので、putなどの編集・追加・削除操作はできないが、ごそっと初期化したい時ってぇのはImmutableで構わないケースがほとんどなので問題はないだろう。

ただ”ガーベージコレクションされない”が致命的でない場合は、double-braceも便利な書き方ではあるので、例えば単発Java Applicationのループ外での初期設定とかでは十分有効だといえる。

つーか、コレクションの初期化は面倒なのはわかるのだけれど他の言語では割と簡単にできるものであるし、ささっと初期化できないってのはなんだかなーとは思うわけだがまぁそういうものだししょうがない。

Javaの非同期処理

Javaの非同期も随分簡単になって、Thread管理クラスを作ってThreadを起こして終了を拾って〜などとやっていた頃が夢のように。

基本的にはコレクションのstreamをparallelで処理、という具合だが業務系では大抵の処理は
・多重度の設定
・全終了の待機
が必要になる。

で、そんな場合のために、ForkJoinPoolクラスが存在していて、だいたい次のように。

private void foo() throws Exception
{
        Map<String, String> mp = new HashMap<String, String>(){
            {{ put("a", "A"); }}
            {{ put("b", "B"); }}
            {{ put("c", "C"); }}
            {{ put("d", "D"); }}
            {{ put("e", "E"); }}
        };

        var pool = new ForkJoinPool(20);    //多重度をセット
        pool.submit(()->mp.entrySet().parallelStream().forEach((x)->{
            System.out.println(x.getKey() + " : " + x.getValue());
        })).get();    //全終了を待機

        System.out.println("parallel-end");
}

非同期処理が込み入ったものであれば、ラムダ内から処理用の関数を呼ぶようにすれば良く、更に同期処理が必要なのであれば呼ばれる関数にsynchronized修飾子をつけておけば良い。

ま、常駐型のスレッドが必要な場合はやっぱりそれなりに管理用クラスを用意して上げたほうがいぢり易くはあるんだが。