Shoeisha Technology Media

CodeZine(コードジン)

記事種別から探す

知っていると得する! Java 9になってより便利になった改善点

Java 9で「変わること」と、Javaのこれまで 第6回

  • LINEで送る
  • このエントリーをはてなブックマークに追加
2018/03/29 14:00

 本連載ではこれまで、Java 9における比較的大きな変更点を紹介してきました。今回は、まだ紹介していない比較的細かな変更点を中心に紹介します。例を挙げると、「CollectionやStream処理の記述の改善」「Process API」「System.Loggerの追加」などです。これらを知らなくても代替手段があるため、それほど注意深く調べることはないかもしれません。しかし、知っていればより便利な使い方ができるはずです。

目次

直感的なCollection作成とStream処理の改善

 これまでよりも、CollectionやStream処理の記述が簡単になりました。

直感的なCollection作成

 ListやSet、もしくはMapといったコレクションのオブジェクトが、リスト1のように簡単に作成できるようになりました。

リスト1 Collectionの作成方法例(src/main/com/coltware/part6/jep269/CollectionUpdates.javaの抜粋)
// (1) 追加するアイテムを順番に記述する
final Set<Object> of = Set.of("a","b","c");
final List<String> list = List.of("l1","l2","l3");

// (2) Map形式はKey,Value,Key,Valueのように続けて記述することが可能
final Map<String,String> map1 = Map.of("k1","v1","k2","v2");

// (3) このように記述することも可能です。
final Map<String,String> map2 = Map.ofEntries(Map.entry("k1","v1"),Map.entry("k2","v2"));

 (1)の通り、かなり直感的な記述ができるようになっています。また、Map形式の場合には(2)のようにKeyとValueを順番につなげることもできます。また少々面倒になりますが、この記述方法以外にも(3)のように記述することもできます。

 リスト1ではコレクション作成時にfinalをつけて宣言していますが、この宣言の有無にかかわらず、作成したコレクションの変更はできません。

Stream処理の改善

 Stream処理でも便利なメソッドがいくつか追加されました。

iterateメソッド

 まず、iterateメソッドです。このiterateメソッドは無限ストリームだけでなく、for文のように終了条件付きで記述することができます。

 リスト2はJava 9とそれ以前で、1から9までの数を表示するときの例です。

リスト2 終了条件を指定できるようになったiterateメソッド(src/main/com/coltware/part6/jep269/CollectionUpdates.javaの抜粋)
//  Java 9での記述例
Stream.iterate(1,i -> i < 10,i -> i + 1).forEach(System.out::println);
//  Java 9以前での記述例
Stream.iterate(1,i -> i + 1).limit(9).forEach(System.out::println);

ofNullableメソッド

 ofNullableメソッドが新たに追加されました。このメソッドを使えばnull以外のデータを扱う際に、リスト3の記述が可能です。

リスト3 null以外の数値一覧を表示する例(src/main/com/coltware/part6/jep269/CollectionUpdates.javaの抜粋)
//  (1) Java 9での記述例
Stream.of(null,7,2,4,3,5,null,6).flatMap(i -> Stream.ofNullable(i)).forEach(System.out::println);
//  (2) Java 9以前での記述例
Stream.of(null,7,2,4,3,5,null,6).flatMap(i -> {
  if(i == null){
     return Stream.empty();
  }
  else{
     return Stream.of(i);
  }
}).forEach(System.out::println);

takeWhile/dropWhileメソッド

 続いては、takeWhileメソッドdropWhileメソッドです。

 takeWhileは条件に一致している間のみ処理を実施し、条件に合わなくなると、それ以降は処理を行ないません。dropWhileは反対に、条件に一致する間のみ処理をせず、条件に一致しないデータが現れて以降、処理をする場合に利用できます。

 例えば、1から9までの数値が連続するようなケースを想定したケースにおいて、takeWhileとdropWhileの振る舞いを示す例がリスト4です。この例ではイメージしやすいようにデータを少数にしていますが、無限ストリームなどですべてのデータを処理せずに済むので、効率よくデータを処理できます。

リスト4 1から4以外の値が現れた時点で、その前のデータでストリーム処理をする(src/main/com/coltware/part6/jep269/CollectionUpdates.javaの抜粋)
// (1) 1,2,3,4が出力される
Stream.of(1,2,3,4,5,6,7,8,9,1,2,3).takeWhile( i -> {
  if( i > 0 && i < 5) {
    return true;
  }
  else{
    return false;
  }
}).forEach(System.out::println);

// (2) 5,6,7,8,9,1,2,3が出力される
Stream.of(1,2,3,4,5,6,7,8,9,1,2,3).dropWhile( i -> {
  if( i > 0 && i < 5) {
    return true;
  }
  else{
    return false;
  }
}).forEach(System.out::println);

 (1)のtakeWhileでは、最初の1から4までのデータを出力しています。2回目に現れる1から3までの値は、その前に条件に一致しないデータである5が現れた時点で処理の対象にならないので出力されません。

 また(2)では同じデータ、条件をdropWhileに置き換えたときの処理です。この場合は、条件に一致しない1から4までのデータではない5が現れた時点からが処理の対象になり、2回目に現れる1から3も処理の対象になります。

改善されたProcess API

 Javaではネイティブのプロセスを扱うため、ProcessクラスやProcessBuilderクラスなどが存在します。ですが、これだけでは実現できないケースもあります。

 例えば、自分でデーモン化するプログラムを作っていると、起動時に自分でPIDファイルを作成したいことがあります。しかし、自分自身のプロセスIDは簡単に取得できないので、JMXなどを利用するなど、ちょっとした工夫が必要でした。Java 9ではプロセスに関する情報を取得するためのProcessHandleインターフェースが追加され、リスト5のようにプロセスに関する情報が取得できます。

リスト5 プロセスIDを調べる(src/main/com/coltware/part6/jep102/ProcessApi.javaの抜粋)
//  (1) 自分自身のプロセスを取得
ProcessHandle handle = ProcessHandle.current();
long pid = handle.pid();

//  (2) 親プロセスを取得
ProcessHandle parent = handle.parent().get();

//  (3) プロセスIDから取得する
ProcessHandle handle2 = ProcessHandle.of(pid).get();

//  (4) プロセスに関する情報を取得する
ProcessHandle.Info info = handle2.info();
String command = info.command().get();
String user  = info.user().get();

 自分自身のプロセスを取得するには(1)の通りProcessHandleのcurrentメソッドで取得します。親プロセスを取得するには(2)のようにparentメソッドが用意されています。これ以外にもプロセス終了の指示や、プロセスが生存しているかなどの確認も可能です。また、あらかじめプロセスIDがわかっている場合には、(3)の通りプロセスIDを指定して情報を取得することも可能です。

 また、(4)のようにプロセスに関する情報を取得するためのProcessHandle.Infoインターフェースも追加されていて、以下の情報が取得できます。

  • プロセスを起動したときの引数(argumentsメソッド)
  • プロセスを実行したコマンド名(commandメソッド)
  • プロセスを起動したときのコマンドライン(commandLineメソッド)
  • プロセスの開始時間(startInstantメソッド)
  • プロセスの合計cpu時間(totalCpuDurationメソッド)
  • プロセスを起動したユーザ(userメソッド)

 またProcessBuilderクラスでは、これまでstartメソッドを使って外部プログラムを実行していました。今回、startPipelineメソッドが追加され、パイプ(|)を用いたプロセスの処理が簡単に実行できるようになりました。

 Javaに関するプロセスが現在いくつあるかを調べる処理について、パイプを使って実行した際の例がリスト6です。

リスト6 プロセスIDを調べる(src/main/com/coltware/part6/jep102/ProcessApi.javaの抜粋)
// (1) プロセス一覧から結果から、"java"という文字を含む行数を取得する
// $ps -ef | grep java | wc -l

// (2) ProcessBuilderでのstartPipelineメソッドを使う場合

// (3) "ps -ef"を実行するプロセスを起動する処理
ProcessBuilder ps = new ProcessBuilder().command("ps","-ef");
// (4) "grep java"を実行するプロセスを起動する処理
ProcessBuilder grep = new ProcessBuilder().command("grep","java");
// (5) "wc -l"を実行するプロセスを起動する処理
ProcessBuilder wc = new ProcessBuilder().command("wc","-l");
try {

  // (6) それぞれのプロセスをパイプでつなげて実行する
  List<Process> processes = ProcessBuilder.startPipeline(List.of(ps,grep,wc));
  // (7) 最後のプロセス(wc)を取得し、そのプロセスの終了を待つ
  Process p = processes.get(processes.size() - 1);
  int wait = p.waitFor();
  // (8)  wcプロセスの出力を表示する
  BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
    String line;
    while((line = reader.readLine()) != null ) {
        System.out.println(line);
    }
  }
  catch(Exception ex){
}

 (1)はUnix系のOSでパイプを使ってコマンドを実行したときの例です。この処理と同じようにパイプを使ってJavaで実行したのが(2)以降の処理になります。

 (3)(4)(5)では各プロセスを起動するための準備をします。そして、(6)のようにstartPipelineメソッドを使ってそれらを起動します。(7)では最後のプロセスの終了を待ち、(8)ではその結果を標準出力に表示しています。

 Javaのコードにすると記述するコードは増えてしまいますが、コマンドライン上での実行時と似た流れで処理を記述できます。


  • LINEで送る
  • このエントリーをはてなブックマークに追加

著者プロフィール

  • WINGSプロジェクト 小林 昌弘(コバヤシ マサヒロ)

    <WINGSプロジェクトについて> 有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2017年5月時点での登録メンバは52名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい...

  • 山田 祥寛(ヤマダ ヨシヒロ)

    静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for ASP/ASP.NET。執筆コミュニティ「WINGSプロジェクト」代表。 主な著書に「入門シリーズ(サーバサイドAjax/XMLD...

バックナンバー

連載:Java 9で「変わること」と、Javaのこれまで
All contents copyright © 2005-2018 Shoeisha Co., Ltd. All rights reserved. ver.1.5