この記事は「ベストプラクティスを自らしめす」というよりは、「ベストプラクティスがよくわからんので、とりあえずQiitaに書いておけば、詳しい人が教えてくれるだろう」という他力本願的な動機に基づいています(´・ω・`) インターネットの集合知にすがってみるテスト。
まずインターネット上のブログ記事などでよく見つかるのがBufferedReaderを利用するもの。
var stdin = new BufferedReader(new InputStreamReader(System.in));
// 標準入力を1行ずつ読み出す。
String line;
while ((line = stdin.readLine()) != null) {
doSomething(line);
}
// 標準入力から与えられる値が数値の場合は自ら変換する必要がある。
while ((line = stdin.readLine()) != null) {
int i = Integer.valueOf(line);
doSomething(i);
}
// streamにつなげやすい。
stdin.lines().forEach(System.out::println);
stdin.lines().mapToInt(Integer::valueOf);
この方式のメリット・デメリットを簡単にまとめると、
- ネット上でサンプルが見つけやすい。この「サンプルが豊富」というのは実務プログラミングでは意外とばかにならない。
- ただし後述するScannerに比べると、タイプ数が多く、スクリプトレベルのプログラミングだと、やや面倒。
- コンストラクタを2回書くあたりは、理解していても、美しいとは言い難い(´・ω・`)
- Java8より導入されたStreamへつなげやすい。
- バッファリングしているので、高速に動作しそう(未確認)
- 検査例外IOExceptionを処理する必要がある。ちょっとしたスクリプトレベルだとこれはちょっぴり邪魔くさい。
ところで個人的な話をすると、Javaで標準入力を使うのは競技プログラミングが9割。要は「ちゃっちゃと書いて、ちゃっちゃと動かしたい」という場合が多い。また競技プログラミングにありがちなのが「1行目は数値で、2行以降は文字列」というような入力形式で、こういうときにはさまざまな型の入力を手早くさばく必要があります。こういう場合にはScannerのほうが便利であると感じることが多いように思います。
var stdin = new Scanner(System.in);
// 標準入力を1行ずつ読み出す。
while (stdin.hasNextLine()) {
var line = stdin.nextLine();
doSomething(line);
}
// たとえば次のような入力があるとする ---> 1000 10.005 HOGE
// このような複雑な入力でも比較的処理しやすい。
int i = stdin.nextInt();
double d = stdin.nextDouble();
String s = stdin.nextLine();
// Streamにはややつなげづらい。mapToObjなどを利用するには、ひと工夫必要になる。
stdin.forEachRemaining(System.out::println);
こちらについても、メリット・デメリットをまとめておきます。
- とにかく多機能。複雑な入力を処理しやすい。
- ただ「多機能すぎてよくわからん」と思わなくはない。競技プログラミングならともかく、実務ではそれほど複雑な入力があるかと問われると疑問。
- 検査例外を投げないため、「ちゃっちゃと書いて、ちゃっちゃと動かしたい」という場合には便利。
- Streamにはつなげづらい。ConsumerはともかくFunctionを利用しづらいのはつらい。
- バッファリングしているのかは実装依存。多分していない(未確認)
こう書くと、両者ともにメリット・デメリットがあるようです(当たり前)。ちょっとしたスクリプトレベルのプログラミングだと後者のほうが楽ちんですが、前者のほうが現代的というのは間違いない。実務的なプログラミングでは前者を採用するのがよさそうですね(´・ω・`)