ストリームとNIO

このレッスンでは、JavaのストリームベースIOとNIOによる低レベルファイル操作を学びます。

IOストリームアーキテクチャ

TEXT
入力ストリーム(読み取り)
InputStream(バイト入力)
├── FileInputStream(ファイル)
├── BufferedInputStream(バッファ)
├── ByteArrayInputStream(バイト配列)
└── DataInputStream(プリミティブ型)

Reader(文字入力)
├── FileReader(ファイル)
├── BufferedReader(バッファ)
└── InputStreamReader(変換)

出力ストリーム(書き込み)
OutputStream(バイト出力)
├── FileOutputStream(ファイル)
├── BufferedOutputStream(バッファ)
└── DataOutputStream(プリミティブ型)

Writer(文字出力)
├── FileWriter(ファイル)
├── BufferedWriter(バッファ)
└── OutputStreamWriter(変換)

バイトストリーム

FileInputStreamによる読み取り

JAVA
import java.io.FileInputStream;
import java.io.IOException;

public class FileInputStreamDemo {
    public static void main(String[] args) {
        try (FileInputStream fis = new FileInputStream("test.txt")) {
            int data;
            while ((data = fis.read()) != -1) {
                System.out.print((char) data);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

FileOutputStreamによる書き込み

JAVA
import java.io.FileOutputStream;
import java.io.IOException;

public class FileOutputStreamDemo {
    public static void main(String[] args) {
        try (FileOutputStream fos = new FileOutputStream("output.txt")) {
            String text = "Hello, World!";
            fos.write(text.getBytes());
            System.out.println("書き込み成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

例:バイトストリームによるファイルコピー

JAVA
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class ByteCopy {
    public static void copy(String src, String dest) throws IOException {
        try (FileInputStream fis = new FileInputStream(src);
             FileOutputStream fos = new FileOutputStream(dest)) {
            
            byte[] buffer = new byte[1024];
            int length;
            while ((length = fis.read(buffer)) != -1) {
                fos.write(buffer, 0, length);
            }
        }
    }
    
    public static void main(String[] args) {
        try {
            copy("source.txt", "dest.txt");
            System.out.println("コピー完了");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
▶ 試してみよう

バッファストリーム

バッファストリームはディスクアクセス回数を減らし、IO効率を向上させます。

BufferedInputStream/BufferedOutputStream

JAVA
import java.io.*;

public class BufferedStreamDemo {
    public static void main(String[] args) throws IOException {
        // 書き込み
        try (BufferedOutputStream bos = new BufferedOutputStream(
                new FileOutputStream("buffered.txt"))) {
            for (int i = 0; i < 1000; i++) {
                bos.write(("Line " + i + "\n").getBytes());
            }
        }
        
        // 読み取り
        try (BufferedInputStream bis = new BufferedInputStream(
                new FileInputStream("buffered.txt"))) {
            byte[] buffer = new byte[1024];
            int length;
            while ((length = bis.read(buffer)) != -1) {
                System.out.write(buffer, 0, length);
            }
        }
    }
}

データストリーム

DataInputStream/DataOutputStreamはプリミティブ型を読み書きできます。

例:データストリーム

JAVA
import java.io.*;

public class DataStreamDemo {
    public static void main(String[] args) throws IOException {
        // 書き込み
        try (DataOutputStream dos = new DataOutputStream(
                new FileOutputStream("data.dat"))) {
            dos.writeInt(100);
            dos.writeDouble(3.14);
            dos.writeUTF("Hello");
        }
        
        // 読み取り
        try (DataInputStream dis = new DataInputStream(
                new FileInputStream("data.dat"))) {
            int i = dis.readInt();
            double d = dis.readDouble();
            String s = dis.readUTF();
            
            System.out.println("int: " + i);      // 100
            System.out.println("double: " + d);    // 3.14
            System.out.println("string: " + s);    // Hello
        }
    }
}
▶ 試してみよう

NIO(New IO)

Java NIOはより効率的なIO操作を提供します。

Pathインターフェース

JAVA
import java.nio.file.Path;
import java.nio.file.Paths;

public class PathDemo {
    public static void main(String[] args) {
        Path path = Paths.get("/home/user/test.txt");
        
        System.out.println("ファイル名: " + path.getFileName());  // test.txt
        System.out.println("親ディレクトリ: " + path.getParent());    // /home/user
        System.out.println("ルート: " + path.getRoot());      // /
        System.out.println("名前数: " + path.getNameCount());  // 3
    }
}

Filesユーティリティクラス

JAVA
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;

public class FilesDemo {
    public static void main(String[] args) throws IOException {
        Path path = Paths.get("test.txt");
        
        // チェック
        System.out.println("存在する: " + Files.exists(path));
        System.out.println("ファイルか: " + Files.isRegularFile(path));
        System.out.println("読み取り可能: " + Files.isReadable(path));
        
        // すべての行を読み取る
        List<String> lines = Files.readAllLines(path);
        lines.forEach(System.out::println);
        
        // バイト配列として読み取る
        byte[] bytes = Files.readAllBytes(path);
        System.out.println("サイズ: " + bytes.length);
        
        // 書き込み
        Files.write(Paths.get("output.txt"), "Hello".getBytes());
        
        // ディレクトリ作成
        Files.createDirectories(Paths.get("newdir/subdir"));
        
        // コピー
        Files.copy(Paths.get("source.txt"), Paths.get("dest.txt"));
        
        // 移動
        Files.move(Paths.get("old.txt"), Paths.get("new.txt"));
        
        // 削除
        Files.deleteIfExists(Paths.get("temp.txt"));
    }
}

ファイル属性

JAVA
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;

public class FileAttributes {
    public static void main(String[] args) throws IOException {
        Path path = Paths.get("test.txt");
        
        BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
        
        System.out.println("サイズ: " + attrs.size());
        System.out.println("作成日時: " + attrs.creationTime());
        System.out.println("更新日時: " + attrs.lastModifiedTime());
        System.out.println("ディレクトリか: " + attrs.isDirectory());
        System.out.println("ファイルか: " + attrs.isRegularFile());
    }
}

ファイル走査(NIO)

JAVA
import java.io.IOException;
import java.nio.file.*;
import java.util.stream.Stream;

public class NIODirectoryWalk {
    public static void main(String[] args) throws IOException {
        Path dir = Paths.get(".");
        
        // ディレクトリを一覧表示
        try (Stream<Path> stream = Files.list(dir)) {
            stream.forEach(path -> {
                String type = Files.isDirectory(path) ? "[DIR]" : "[FILE]";
                System.out.println(type + " " + path.getFileName());
            });
        }
        
        // 再帰的に走査
        try (Stream<Path> stream = Files.walk(dir, 3)) {  // 最大3レベル
            stream.forEach(path -> System.out.println(path));
        }
        
        // ファイルを検索
        try (Stream<Path> stream = Files.find(dir, 10, 
                (path, attrs) -> attrs.isRegularFile() && path.toString().endsWith(".txt"))) {
            stream.forEach(System.out::println);
        }
    }
}

IO vs NIO

特徴 IO NIO
モデル ストリーム指向 バッファ指向
ブロッキング ブロッキングIO 非ブロッキングIO
セレクタ なし あり
API 古い 新しい

選択ガイド

シナリオ 推奨
簡単なファイル読み書き Filesユーティリティクラス
大きなファイル処理 バッファストリーム
高並行ネットワーク NIO
レガシーコード保守 IOストリーム

❓ よくある質問

Q バイトストリームと文字ストリームのどちらを選ぶべきですか?
A テキストファイルには文字ストリームを使用し、バイナリファイルにはバイトストリームを使用してください。
Q バッファストリームの利点は何ですか?
A ディスクアクセス回数を減らし、IO効率を向上させます。
Q NIOはIOより速いですか?
A 必ずしもそうではありません。NIOの主な利点は非ブロッキングとセレクタです。簡単なファイル操作では差は最小限です。

📖 まとめ

📝 演習

  1. ファイル比較: 2つのファイルが同一かどうかを比較
  2. 大きなファイル処理: 大きなファイルをチャンクで読み取り、行数をカウント
  3. ディレクトリ同期: 1つのディレクトリを別のディレクトリに同期

次のレッスン

次のレッスンでは、正規表現とJSONを学びます — 高度な文字列処理技術。

100%