ファイルの行ストリーム処理(Files.lines) — 大規模ファイル処理
Java NIO の Files.lines(Path) は、テキストファイルを 1行ずつストリームとして処理できる便利なメソッドです。大規模ファイルでも全体を一度に読み込まず、遅延読み込みで効率的に扱えるのが特徴です。
基本の仕組み
- 戻り値:
Stream<String>(各要素が1行の文字列) - 遅延読み込み: 必要な行だけ逐次読み込むため、大きなファイルでもメモリ効率が良い。
- 注意点: ストリームは I/O リソースを持つので、必ず try-with-resources で閉じる。
基本コード例
ファイルを1行ずつ出力
import java.nio.file.*;
import java.io.IOException;
import java.util.stream.Stream;
public class FileLinesBasic {
public static void main(String[] args) throws IOException {
Path path = Paths.get("data.txt");
try (Stream<String> lines = Files.lines(path)) {
lines.forEach(System.out::println);
}
}
}
Java- ポイント:
try-with-resourcesで自動的にストリームを閉じる。
フィルタして処理
Path path = Paths.get("data.txt");
try (Stream<String> lines = Files.lines(path)) {
lines.filter(l -> l.contains("ERROR"))
.forEach(System.out::println);
}
Java- ポイント: ログファイルから「ERROR」を含む行だけ抽出。
map を使って変換
try (Stream<String> lines = Files.lines(Paths.get("numbers.txt"))) {
int sum = lines.mapToInt(Integer::parseInt).sum();
System.out.println("合計=" + sum);
}
Java- ポイント: 数字が並んだファイルを読み込み、合計を計算。
例題で理解する
例題1: 大規模ログから統計を取る
Path logFile = Paths.get("server.log");
try (Stream<String> lines = Files.lines(logFile)) {
long errorCount = lines.filter(l -> l.contains("ERROR")).count();
System.out.println("ERROR件数=" + errorCount);
}
Java- 用途: 数百万行のログでもメモリに全部載せずに処理可能。
例題2: CSV ファイルから特定列を抽出
Path csv = Paths.get("users.csv");
try (Stream<String> lines = Files.lines(csv)) {
lines.map(l -> l.split(","))
.map(cols -> cols[1]) // 2列目(名前)
.forEach(System.out::println);
}
Java- 用途: 大規模 CSV から必要な列だけ取り出す。
例題3: ファイルからユニークな単語を抽出
Path text = Paths.get("book.txt");
try (Stream<String> lines = Files.lines(text)) {
lines.flatMap(l -> Arrays.stream(l.split("\\s+")))
.map(String::toLowerCase)
.distinct()
.sorted()
.forEach(System.out::println);
}
Java- 用途: 書籍テキストからユニークな単語リストを作成。
テンプレート集
- 基本読み込み
try (Stream<String> lines = Files.lines(Paths.get("file.txt"))) {
lines.forEach(System.out::println);
}
Java- フィルタ+件数
long count = Files.lines(Paths.get("file.txt"))
.filter(s -> 条件)
.count();
Java- map で変換
List<Integer> nums = Files.lines(Paths.get("file.txt"))
.map(Integer::parseInt)
.toList();
Java- flatMap で単語展開
Files.lines(Paths.get("file.txt"))
.flatMap(line -> Arrays.stream(line.split("\\s+")))
.forEach(System.out::println);
Java落とし穴と回避策
- リソース管理:
Files.linesはファイルを開いたままなので、必ず try-with-resources で閉じる。 - 文字コード: デフォルトは UTF-8。別の文字コードなら
Files.lines(path, charset)を使う。 - 大規模ファイル: 全行を
collect(toList())するとメモリに載ってしまう。逐次処理(forEach, count, sum)で流すのが安全。 - 例外処理: IOException が発生する可能性があるので、throws か try-catch を必ず書く。
まとめ
Files.linesは「大規模ファイルを1行ずつストリーム処理」するための定番。- フィルタ・map・flatMap と組み合わせると、ログ解析や CSV 処理が簡潔に書ける。
- try-with-resources で安全に閉じること、メモリに全行をため込まないことが重要。
👉 練習課題: 「数百万行のログファイルから、ERROR を含む行を抽出して件数を数える」コードを書いてみると、Files.lines の強みが体感できます。
