- JAVA
Java Scannerの使い方|nextLineとnextIntの違いと改行残り
Scannerとは、キーボード入力(標準入力)やファイル・文字列から、文字列や数値を1つずつ取り出すためのクラスです。 new Scanner(System.in) で作り、1行まるごと読むなら nextLine()、整数を1つ読むなら nextInt()、空白で区切られた単語を1つ読むなら next() を使います。
Scannerでいちばん多い質問は、「nextInt() のあとに nextLine() を呼ぶと、入力を待たずに空文字が返ってくる」というものです。この記事では、その現象をOpenJDK 21.0.10で再現し、3通りの対処を実行結果つきで比べます。あわせて、hasNextInt を使った入力チェック、InputMismatchException の実際のスタックトレース、複数行・空白区切りの入力パターン集、BufferedReader との速度比較、System.in を閉じるときの注意まで、1本でたどれるようにしました。Java 25での差分は公式APIドキュメントで確認して注記しています。
Scannerの早見表
| やりたいこと | メソッド | 読む範囲 | 読んだあとの位置 |
|---|---|---|---|
| 1行まるごと読む | nextLine() |
行末の改行の手前まで(空白も含む) | 次の行の先頭(改行も消費する) |
| 単語を1つ読む | next() |
次の空白・改行の手前まで | 単語の直後(改行は残る) |
| 整数を読む | nextInt() |
整数1つ | 数字の直後(改行は残る) |
| 大きな整数を読む | nextLong() |
整数1つ(long の範囲) |
数字の直後 |
| 小数を読む | nextDouble() |
小数1つ | 数字の直後 |
| true/falseを読む | nextBoolean() |
true か false |
直後 |
| 次が整数か確かめる | hasNextInt() |
読まない(確認だけ) | 変わらない |
| 次のトークンがあるか | hasNext() |
読まない | 変わらない |
| 次の行があるか | hasNextLine() |
読まない | 変わらない |
| 1文字読む | next().charAt(0) |
単語の1文字目 | 単語の直後 |
| 区切り文字を変える | useDelimiter(",") |
― | ― |
| よくある症状 | 原因 | 対処 |
|---|---|---|
nextInt() の次の nextLine() が空文字になる |
数字のあとの改行が残っている | nextLine() を1回はさんで読み捨てる |
InputMismatchException |
整数でない入力を nextInt() で読んだ |
hasNextInt() で先に確かめる、または行で読んで Integer.parseInt |
NoSuchElementException |
入力が尽きた、または System.in が閉じられている |
hasNext() で確かめる。Scannerは1つを使い回す |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Java Scannerの使い方|nextLineとnextIntの違いと改行残り」
まず動かす:名前と年齢を読む基本コード
Scannerを使うには java.util.Scanner をimportし、new Scanner(System.in) で標準入力につなぎます。 下のコードを Hello.java として保存し、java Hello.java で実行します。
import java.util.Scanner;
public class Hello {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("名前: ");
String name = sc.nextLine(); // 1行まるごと
System.out.print("年齢: ");
int age = sc.nextInt(); // 整数1つ
System.out.println();
System.out.println(name + "さんは" + age + "歳です");
}
}
実行結果(OpenJDK 21.0.10。入力は printf '山田 太郎\n28\n' | java Hello.java で渡しています):
名前: 年齢:
山田 太郎さんは28歳です
入力をパイプで渡したため、打ち込んだ文字は画面に出ていません。ターミナルで手入力した場合は、名前: のあとに自分で打った文字が表示され、Enterを押すと次へ進みます。この記事の実行結果は、すべて同じようにパイプやファイルで入力を渡して取得したものです。
nextLine()は空白を含めて1行を読むので、「山田 太郎」がそのまま取れます。next()で読むと「山田」までになります。System.inは「標準入力」を表します。 キーボードだけでなく、java Hello.java < input.txtのようにファイルをつないでも同じコードで読めます。- 日本語の入力が文字化けするときは、
new Scanner(System.in, "UTF-8")のように、第2引数でターミナル側の文字コード名を指定します(Java 18以降は、指定しない場合の既定がUTF-8です)。
nextLine・next・nextIntの違い
Scannerは入力を「トークン(空白や改行で区切られたかたまり)」に分けて読みます。 next() と nextInt() などの nextXxx はトークン単位、nextLine() だけが行単位で読む、というのが違いの中心です。
次の入力を、いろいろなメソッドで順に読んでみます。
red blue green
42 3.14
9999999999 true 0.10
ff tail
last line
import java.util.Scanner;
public class Tokens {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("next() = [" + sc.next() + "]");
System.out.println("nextLine() = [" + sc.nextLine() + "]");
System.out.println("nextInt() = " + sc.nextInt());
System.out.println("nextDouble()= " + sc.nextDouble());
System.out.println("nextLong() = " + sc.nextLong());
System.out.println("nextBoolean = " + sc.nextBoolean());
System.out.println("nextBigDecimal = " + sc.nextBigDecimal());
System.out.println("nextInt(16) = " + sc.nextInt(16));
System.out.println("nextLine() = [" + sc.nextLine() + "]");
System.out.println("nextLine() = [" + sc.nextLine() + "]");
System.out.println("hasNext() = " + sc.hasNext());
System.out.println("hasNextLine()= " + sc.hasNextLine());
}
}
実行結果(OpenJDK 21.0.10):
next() = [red]
nextLine() = [ blue green]
nextInt() = 42
nextDouble()= 3.14
nextLong() = 9999999999
nextBoolean = true
nextBigDecimal = 0.10
nextInt(16) = 255
nextLine() = [ tail]
nextLine() = [last line]
hasNext() = false
hasNextLine()= false
| メソッド | 読む単位 | 前にある空白・改行 | 後ろの改行 | 戻り値 |
|---|---|---|---|---|
next() |
トークン | 飛ばす | 残す | String |
nextInt() / nextLong() |
トークン | 飛ばす | 残す | int / long |
nextDouble() |
トークン | 飛ばす | 残す | double |
nextBoolean() |
トークン | 飛ばす | 残す | boolean |
nextBigDecimal() |
トークン | 飛ばす | 残す | BigDecimal |
nextLine() |
行 | 飛ばさない(そのまま含める) | 消費する(戻り値には含めない) | String |
実行結果の2行目に注目してください。next() で red を読んだあとの nextLine() は、同じ行の残り blue green(先頭に空白つき)を返しています。 nextLine() は「次の行」を読むメソッドではなく、「今いる位置から行末まで」を読むメソッドです。これが、次の節で扱う改行残りの正体です。
なお、nextChar() というメソッドはありません。1文字だけ読みたいときは sc.next().charAt(0) と書きます。
nextIntのあとのnextLineが飛ばされる現象を再現する
nextInt() のあとに nextLine() を呼ぶと、入力を待たずに空文字が返ります。 年齢を読んでから名前を読むプログラムで再現します。
import java.util.Scanner;
public class Skip {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int age = sc.nextInt();
String name = sc.nextLine(); // ここが空文字になる
System.out.println("age=" + age);
System.out.println("name=[" + name + "] 長さ=" + name.length());
System.out.println("次の行=[" + sc.nextLine() + "]");
}
}
実行結果(OpenJDK 21.0.10。入力は 28 と 山田 太郎 の2行):
age=28
name=[] 長さ=0
次の行=[山田 太郎]
name が長さ0の空文字になり、本来読みたかった「山田 太郎」は、その次の nextLine() で読まれています。
なぜ空文字になるのか
入力は、Scannerから見ると改行(\n)を含んだ1本の文字の並びです。
2 8 \n 山 田 太 郎 \n
| 手順 | 呼び出し | 読んだ部分 | 残り |
|---|---|---|---|
| 1 | nextInt() |
28 |
\n山田 太郎\n |
| 2 | nextLine() |
位置から行末まで=空文字(\n を消費) |
山田 太郎\n |
| 3 | nextLine() |
山田 太郎(\n を消費) |
なし |
nextInt() は数字だけを読み、Enterで入った改行を残します。 続く nextLine() は「今の位置から行末まで」を返すので、残っていた改行だけを消費して空文字を返します。Scannerが入力を読み飛ばしたのではなく、仕様どおりに動いた結果です。next()・nextDouble()・nextLong() など、トークン単位で読むメソッドのあとでも同じことが起きます。
対処1:nextLineを1回はさんで読み捨てる
int age = sc.nextInt();
sc.nextLine(); // 残った改行を読み捨てる
String name = sc.nextLine();
System.out.println("age=" + age + " name=[" + name + "]");
age=28 name=[山田 太郎]
対処2:すべてnextLineで読み、数値は変換する
int age = Integer.parseInt(sc.nextLine().trim()); // 行で読んでから変換
String name = sc.nextLine();
System.out.println("age=" + age + " name=[" + name + "]");
age=28 name=[山田 太郎]
対処3:あとの読み取りもnextにそろえる
int age = sc.nextInt();
String name = sc.next(); // 空白・改行を飛ばして次の単語を読む
System.out.println("age=" + age + " name=[" + name + "]");
age=28 name=[山田]
(3つとも Skip.java の main の中身を差し替え、同じ2行の入力で実行した結果です。)
3通りの比較と選び方
| 対処 | 書き方 | 空白を含む文字列 | 向いている場面 |
|---|---|---|---|
| 1. 読み捨て | sc.nextInt(); sc.nextLine(); |
読める | 既存のコードを最小の変更で直す |
| 2. 行で統一 | Integer.parseInt(sc.nextLine().trim()) |
読める | 対話型の入力。1行に1つの値を入れてもらう |
3. next で統一 |
sc.nextInt(); sc.next(); |
読めない(最初の単語まで) | 空白区切りのデータ。文字列に空白が入らない |
対話型のプログラムでは、対処2の「全部 nextLine() で読む」がいちばん安定します。 改行の位置を気にする必要がなくなり、数値でない入力があったときも、その行をまるごと捨ててやり直せるからです。対処3は、実行結果のとおり「山田 太郎」が「山田」までになる点に注意します。
nextIntに数字以外を入れるとInputMismatchException
nextInt() に整数として読めない入力が来ると、InputMismatchException が発生します。
import java.util.Scanner;
public class Mismatch {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
System.out.println(n);
}
}
abc を入力したときの実行結果(OpenJDK 21.0.10):
Exception in thread "main" java.util.InputMismatchException
at java.base/java.util.Scanner.throwFor(Scanner.java:947)
at java.base/java.util.Scanner.next(Scanner.java:1602)
at java.base/java.util.Scanner.nextInt(Scanner.java:2267)
at java.base/java.util.Scanner.nextInt(Scanner.java:2221)
at Mismatch.main(Mismatch.java:6)
入力を変えて、同じプログラムを実行した結果をまとめます。
| 入力 | 結果(1行目) | 理由 |
|---|---|---|
abc |
java.util.InputMismatchException |
整数の形ではない |
3.5 |
java.util.InputMismatchException |
小数は nextInt() では読めない(nextDouble() なら読める) |
3000000000 |
java.util.InputMismatchException: For input string: "3000000000" |
int の上限(2147483647)を超えている。nextLong() なら読める |
| (入力なし) | java.util.NoSuchElementException |
読むものがない |
例外を捕まえるだけだと同じ入力を読み続ける
InputMismatchException が出たとき、読めなかったトークンは入力に残ったままです。 try-catch で囲んでループするだけでは、同じトークンを何度も読むことになります。
import java.util.InputMismatchException;
import java.util.Scanner;
public class Loop {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int count = 0;
while (count < 3) {
try {
int n = sc.nextInt();
System.out.println("OK: " + n);
break;
} catch (InputMismatchException e) {
count++;
System.out.println(count + "回目: InputMismatchException(トークンは残ったまま)");
// sc.next(); を入れないと同じ "abc" を読み続ける
}
}
}
}
abc を1回だけ入力したときの実行結果(OpenJDK 21.0.10):
1回目: InputMismatchException(トークンは残ったまま)
2回目: InputMismatchException(トークンは残ったまま)
3回目: InputMismatchException(トークンは残ったまま)
入力は1回なのに、例外が3回出ています。回数の上限(count < 3)を付けていなければ、止まらずに回り続けます。catch の中で sc.next() または sc.nextLine() を呼び、読めなかった入力を捨ててから次へ進むのが直し方です。
hasNextIntで入力をチェックする
hasNextInt() は「次のトークンが整数として読めるか」を、読み進めずに確かめるメソッドです。 例外に頼らずに、数値とそれ以外を振り分けられます。
import java.util.Scanner;
public class HasNextInt {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int sum = 0;
while (sc.hasNext()) {
if (sc.hasNextInt()) {
int v = sc.nextInt();
sum += v;
System.out.println("整数: " + v);
} else {
String bad = sc.next(); // 読み捨てないと同じトークンで止まり続ける
System.out.println("読み飛ばし: " + bad);
}
}
System.out.println("合計: " + sum);
}
}
実行結果(OpenJDK 21.0.10。入力は 10 abc 20 と 3.5 30 の2行):
整数: 10
読み飛ばし: abc
整数: 20
読み飛ばし: 3.5
整数: 30
合計: 60
| メソッド | true になる条件 |
|---|---|
hasNext() |
次のトークンがある |
hasNextInt() |
次のトークンが int として読める |
hasNextLong() |
次のトークンが long として読める |
hasNextDouble() |
次のトークンが double として読める |
hasNextLine() |
まだ読んでいない行がある |
hasNextXxx はキーボード入力の場合、次の入力が来るまで待ちます。ファイルやパイプでは、入力の終わりに達すると false を返します。
正しい値が入るまで聞き直す(コピペ用の完成形)
対話型で「1〜5の整数」を入力してもらうときの、行単位で読む書き方です。数値以外・空行・範囲外のどれが来ても、その行を捨てて聞き直します。
import java.util.Scanner;
public class Retry {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n;
while (true) {
System.out.print("1〜5の整数を入力: ");
if (!sc.hasNextLine()) { // 入力が尽きた(Ctrl+D など)
System.out.println("\n入力が終わりました");
return;
}
String line = sc.nextLine().trim();
try {
n = Integer.parseInt(line);
} catch (NumberFormatException e) {
System.out.println("\n → [" + line + "] は整数ではありません");
continue;
}
if (n < 1 || n > 5) {
System.out.println("\n → " + n + " は範囲外です");
continue;
}
break;
}
System.out.println("\n入力値: " + n);
}
}
実行結果(OpenJDK 21.0.10。入力は abc、空行、9、3 の4行。パイプ入力で見やすくするため、メッセージの前に改行を入れています):
1〜5の整数を入力:
→ [abc] は整数ではありません
1〜5の整数を入力:
→ [] は整数ではありません
1〜5の整数を入力:
→ 9 は範囲外です
1〜5の整数を入力:
入力値: 3
| 方法 | 書き方 | 特徴 |
|---|---|---|
| 行で読んで変換 | Integer.parseInt(sc.nextLine().trim()) を try-catch |
行ごとやり直せる。改行残りが起きない。対話型向き |
| 先に確かめる | if (sc.hasNextInt()) sc.nextInt(); else sc.next(); |
例外を使わない。空白区切りのデータ向き |
| 例外を捕まえる | catch (InputMismatchException e) { sc.next(); } |
読み捨てを忘れると同じ入力を読み続ける |
入力パターン集:複数行・空白区切り・カンマ区切り
空白区切りや複数行のデータは、nextInt() と next() を並べるだけで読めます。 これらは空白も改行も同じ「区切り」として飛ばすので、1行に並んでいても複数行に分かれていても同じコードで読めます。
次の入力(patterns.txt)を、6つのパターンで順に読みます。
3
10 20 30
2 3
1 2 3
4 5 6
2
ペン 40
ノート 12
備品 管理 メモ
A,B,,D
1行目
2行目
import java.util.*;
public class Patterns {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// (A) 1行目に件数N、2行目にN個の整数(空白区切り)
int n = sc.nextInt();
int[] a = new int[n];
for (int i = 0; i < n; i++) a[i] = sc.nextInt();
System.out.println("A: " + Arrays.toString(a));
// (B) H行W列の表
int h = sc.nextInt(), w = sc.nextInt();
int[][] g = new int[h][w];
for (int i = 0; i < h; i++)
for (int j = 0; j < w; j++) g[i][j] = sc.nextInt();
System.out.println("B: " + Arrays.deepToString(g));
// (C) 名前と数値が1行に並ぶ行がM行
int m = sc.nextInt();
Map<String, Integer> stock = new LinkedHashMap<>();
for (int i = 0; i < m; i++) stock.put(sc.next(), sc.nextInt());
System.out.println("C: " + stock);
// (D) 空白を含む1行(直前が nextInt なので読み捨てが必要)
sc.nextLine();
String title = sc.nextLine();
System.out.println("D: [" + title + "]");
// (E) 1行をカンマで分ける
String[] cols = sc.nextLine().split(",");
System.out.println("E: " + Arrays.toString(cols) + " 個数=" + cols.length);
// (F) 個数がわからない行を最後まで
List<String> rest = new ArrayList<>();
while (sc.hasNextLine()) {
String line = sc.nextLine();
if (line.isBlank()) continue;
rest.add(line);
}
System.out.println("F: " + rest);
}
}
実行結果(OpenJDK 21.0.10。java Patterns.java < patterns.txt):
A: [10, 20, 30]
B: [[1, 2, 3], [4, 5, 6]]
C: {ペン=40, ノート=12}
D: [備品 管理 メモ]
E: [A, B, , D] 個数=4
F: [1行目, 2行目]
| 入力の形 | 読み方 |
|---|---|
| 整数が1つ | int n = sc.nextInt(); |
| 1行に整数が2つ(空白区切り) | int a = sc.nextInt(), b = sc.nextInt(); |
| N個の整数(1行でも複数行でも) | for (int i = 0; i < n; i++) a[i] = sc.nextInt(); |
| H行W列の表 | 二重の for で sc.nextInt() |
| 単語と数値の組 | sc.next() と sc.nextInt() を続けて呼ぶ |
| 空白を含む1行 | sc.nextLine()(直前がトークン読みなら1回読み捨てる) |
| カンマ区切りの1行 | sc.nextLine().split(",") |
| 行数がわからない | while (sc.hasNextLine()) { ... sc.nextLine() ... } |
| 値の個数がわからない | while (sc.hasNextInt()) { ... sc.nextInt() ... } |
(D) のように、トークン読みから行読みに切り替えるところだけ、sc.nextLine() の読み捨てが1回必要です。 切り替えの回数が多い入力は、最初から行で読んで split するほうが見通しがよくなります。
区切り文字を変える・文字列やファイルから読む
Scannerは System.in 以外も読めます。useDelimiter で区切りを正規表現で指定でき、文字列を渡せばその文字列を入力として読みます。
Scanner sc = new Scanner("ペン,120,40\nノート,300,12\n").useDelimiter("[,\\n]");
while (sc.hasNext()) {
String name = sc.next();
int price = sc.nextInt();
int stock = sc.nextInt();
System.out.println(name + " 価格=" + price + " 在庫=" + stock);
}
ペン 価格=120 在庫=40
ノート 価格=300 在庫=12
ファイルを読むときは new Scanner(Path.of("memo.txt"), "UTF-8") と書きます。ファイルは使い終わったら閉じたいので、try (Scanner f = new Scanner(Path.of("memo.txt"), "UTF-8")) { ... } の形にします(hasNextLine() と nextLine() で2行のファイルを読めることを確認しました)。
数値の読み取りはロケールの影響を受ける
nextInt() や nextDouble() は、桁区切りや小数点の記号をロケール(地域設定)に合わせて解釈します。 検証環境(ロケール en_US)で確認した結果です。
| 入力 | 呼び出し | 結果 |
|---|---|---|
1,234 |
nextInt() |
1234(カンマを桁区切りとして読む) |
123(全角数字) |
nextInt() |
123 |
2147483648 |
hasNextInt() / hasNextLong() |
false / true |
3.14 |
useLocale(Locale.GERMANY) のあと hasNextDouble() |
false |
3,14 |
useLocale(Locale.GERMANY) のあと nextDouble() |
3.14 |
実行する環境によって小数の読み取り結果が変わるのを避けたいときは、sc.useLocale(Locale.ROOT) のようにロケールを固定するか、行で読んで Double.parseDouble で変換します。
Scannerを閉じるとSystem.inも閉じる
Scanner を close() すると、つないでいる System.in も一緒に閉じられ、そのプログラムの中では二度と標準入力を読めなくなります。 ファイルと同じ感覚で、メソッドごとに try-with-resources で囲むと起きます。
import java.util.Scanner;
public class Close {
static int readOne() {
try (Scanner sc = new Scanner(System.in)) { // メソッドを抜けると System.in も閉じる
return sc.nextInt();
}
}
public static void main(String[] args) {
System.out.println("1回目: " + readOne());
System.out.println("2回目: " + readOne());
}
}
実行結果(OpenJDK 21.0.10。入力は 1 と 2 の2行):
1回目: 1
Exception in thread "main" java.util.NoSuchElementException
at java.base/java.util.Scanner.throwFor(Scanner.java:945)
at java.base/java.util.Scanner.next(Scanner.java:1602)
at java.base/java.util.Scanner.nextInt(Scanner.java:2267)
at java.base/java.util.Scanner.nextInt(Scanner.java:2221)
at Close.readOne(Close.java:6)
at Close.main(Close.java:11)
入力には 2 が残っているのに、2回目の readOne() は NoSuchElementException になりました。閉じたあとの状態も確認しました。
Scanner sc = new Scanner(System.in);
sc.close();
try { sc.nextInt(); } catch (IllegalStateException e) { System.out.println("閉じたScanner: " + e); }
try { System.out.println("System.in.read() = " + System.in.read()); }
catch (java.io.IOException e) { System.out.println("System.in: " + e); }
閉じたScanner: java.lang.IllegalStateException: Scanner closed
System.in: java.io.IOException: Stream closed
公式ドキュメントにも、Scannerを閉じると、入力元が Closeable を実装している場合はその入力元も閉じる、と書かれています。
直し方:Scannerは1つ作って使い回す
import java.util.Scanner;
public class CloseOk {
private static final Scanner IN = new Scanner(System.in); // 1つを使い回す
static int readOne() { return IN.nextInt(); }
public static void main(String[] args) {
System.out.println("1回目: " + readOne());
System.out.println("2回目: " + readOne());
}
}
1回目: 1
2回目: 2
閉じなくても、System.in につないだScannerを複数作ること自体が入力の取りこぼしにつながります。 Scannerは入力を先読みして内部にためるためです。
Scanner a = new Scanner(System.in);
System.out.println("a: " + a.nextInt());
Scanner b = new Scanner(System.in); // 2つ目を作る
System.out.println("b.hasNextInt(): " + b.hasNextInt());
a: 1
b.hasNextInt(): false
入力は 1 と 2 の2行ですが、2 は1つ目のScannerがすでに先読みしているので、2つ目からは見えません。
| 入力元 | 閉じ方 |
|---|---|
System.in |
プログラム全体で1つだけ作り、閉じずに使い回す(閉じるならプログラムの最後に1回だけ) |
ファイル(Path / File) |
try-with-resources で閉じる |
| 文字列 | 閉じなくてよい |
IDEが「リソースが閉じられていません」という警告を出すことがありますが、System.in はJVMが用意した共有の入力なので、上の表の扱いで問題ありません。
ScannerとBufferedReaderの速度を比べる
大量の入力を読むときは、BufferedReader のほうが速く読めます。 1行に1つ整数が入ったファイルを用意し、全部を足し合わせる時間を測りました(事前に javac でコンパイルし、java -cp cls クラス名 < nums.txt で各2回実行)。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class ReadBuffered {
public static void main(String[] args) throws IOException {
long t0 = System.nanoTime();
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(br.readLine().trim());
long sum = 0;
for (int i = 0; i < n; i++) sum += Integer.parseInt(br.readLine().trim());
System.out.printf("BufferedReader: sum=%d %d ms%n", sum, (System.nanoTime() - t0) / 1_000_000);
}
}
Scanner側は、同じ処理を sc.nextInt() で読む版と、Integer.parseInt(sc.nextLine()) で読む版の2つを用意しました。
実行結果(OpenJDK 21.0.10、2コアの検証用コンテナ。JVMの起動後、main の先頭からの経過時間):
| 読み方 | 10万行(1回目 / 2回目) | 100万行(1回目 / 2回目) |
|---|---|---|
Scanner の nextInt() |
567 ms / 527 ms | 1433 ms / 1298 ms |
Scanner の nextLine() + parseInt |
499 ms / 380 ms | 855 ms / 833 ms |
BufferedReader の readLine() + parseInt |
102 ms / 134 ms | 180 ms / 181 ms |
合計値は3つとも一致しました(100万行で 500161506242)。数値は環境で変わりますが、100万行では BufferedReader が Scanner.nextInt() のおよそ7分の1の時間で読み終えています。Scannerは正規表現でトークンを切り出し、ロケールに応じた数値の解釈もしているため、その分の時間がかかります。
BufferedReaderで空白区切りを読む書き方
BufferedReader は行を読むだけなので、空白区切りの値は自分で分けます。
import java.io.*;
import java.util.*;
public class BrSplit {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(br.readLine().trim());
StringTokenizer st = new StringTokenizer(br.readLine());
int[] a = new int[n];
for (int i = 0; i < n; i++) a[i] = Integer.parseInt(st.nextToken());
System.out.println(Arrays.toString(a));
String line;
while ((line = br.readLine()) != null) { // 入力の終わりは null
System.out.println("行: " + line);
}
}
}
実行結果(OpenJDK 21.0.10。入力は 3、10 20 30、あ、い の4行):
[10, 20, 30]
行: あ
行: い
| 比べる点 | Scanner |
BufferedReader |
|---|---|---|
| 数値を直接読む | nextInt() などがある |
ない(Integer.parseInt で変換) |
| 空白区切り | 自動で分ける | split や StringTokenizer で分ける |
| 入力の終わり | hasNext() が false |
readLine() が null |
| 例外 | 非チェック例外(InputMismatchException など) |
IOException(throws か try-catch が必要) |
| 速さ | 手軽さを優先 | 速い |
| 向いている場面 | 学習、対話型の入力、数千行までのデータ | 数十万行以上、処理時間に制限がある課題 |
まずはScannerで書き、入力が数十万行を超えて時間が気になったら BufferedReader に切り替える、という順で選ぶと無理がありません。
Java 25での差分(公式ドキュメントで確認)
この記事のコードはすべてJava 21.0.10で実行しました。Java 25については手元で動かしておらず、Java SE 25の公式APIドキュメントで次の点を確認しています。
Scannerクラスには、Java 22〜25で追加されたコンストラクタ・メソッドはありません。 この記事の内容はJava 25でもそのまま当てはまります。- Java 25では
java.lang.IOクラスが追加され、IO.readln()・IO.readln(String prompt)・IO.println(Object)・IO.print(Object)が使えます。readlnは1行を読んで改行を除いた文字列を返し、入力の終わりではnullを返します。文字コードはstdin.encodingプロパティに従います。 IO.readlnは行単位の読み取りだけなので、空白区切りの数値を読む、hasNextIntで確かめる、といった用途では引き続きScannerが使えます。
// Java 25以降(公式ドキュメントの記載に基づく書き方。この記事では未実行)
String name = IO.readln("名前: ");
int age = Integer.parseInt(IO.readln("年齢: ").trim());
IO.println(name + "さんは" + age + "歳です");
よくある質問
Q. nextLine() と next() はどう使い分けますか?
A. 空白を含む文字列(氏名、文章、タイトル)を読むなら nextLine()、空白で区切られた単語を1つずつ読むなら next() です。next() は前にある空白や改行を飛ばしてくれますが、空白の手前で止まります。
Q. nextInt() のあとに nextLine() を2回書くのは正しい書き方ですか?
A. はい、1回目で残った改行を読み捨て、2回目で本来の行を読む、という意味のある書き方です。コメントで // 改行を読み捨てる と添えておくと意図が伝わります。数値も Integer.parseInt(sc.nextLine().trim()) で読む形にそろえると、読み捨て自体が要らなくなります。
Q. 入力の終わり(EOF)はどうやって判定しますか?
A. while (sc.hasNext()) や while (sc.hasNextLine()) でループします。ファイルやパイプなら入力が尽きた時点で false になります。キーボード入力では、macOS・LinuxはCtrl+D、WindowsはCtrl+Zのあと Enter で入力の終わりを伝えられます。確かめずに読むと、この記事で再現した NoSuchElementException(nextLine() の場合は No line found)になります。
Q. Scannerは複数のスレッドから使えますか? A. 公式ドキュメントに「外部で同期しないかぎり、マルチスレッドでの使用は安全ではない」と書かれています。入力を読むスレッドを1つに決めて、読んだ値をほかのスレッドへ渡す形にするとよいでしょう。
まとめ
nextLine()は行単位、next()とnextInt()などはトークン単位で読む。nextLine()だけが改行を消費する。nextInt()のあとのnextLine()が空文字になるのは、数字のあとの改行が残っているため。nextLine()を1回はさむ、全部nextLine()で読んでparseIntする、next()にそろえる、の3通りで直せる。- 整数でない入力は
InputMismatchException。 読めなかったトークンは残るので、catchの中でsc.next()かsc.nextLine()で捨てる。 hasNextInt()で先に確かめると、例外なしで振り分けられる。 対話型は「行で読んでparseInt、合わなければ聞き直す」が安定する。- 空白区切り・複数行のデータは
nextInt()を並べるだけで読める。 行読みに切り替える箇所だけ読み捨てが必要。 System.inにつないだScannerは1つだけ作って使い回す。close()やtry-with-resourcesで閉じるとSystem.inも閉じ、以後はNoSuchElementExceptionになる。- 大量の入力は
BufferedReaderが速い。 100万行の計測でScanner.nextInt()のおよそ7分の1の時間だった。Java 25でもScannerのAPIは同じで、行読み用にIO.readlnが加わっている。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Java Scannerの使い方|nextLineとnextIntの違いと改行残り」
株式会社RJCでは、Scannerによる標準入力の扱い方のようなJavaの基礎を仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。