入出力

Javaは、ファイルの種類によって、そのファイルの入出力に適したファイル処理用のクラスが多く提供されています。

ファイルの入出力には、大きく2系統あり1つはJava1.0からJava6まで古くから使われているjava.ioパッケージです。
もう1つは、Java7から導入されたFeaturing NIO.2とよばれるjava.nioパッケージになります。

まずは、古くから利用されているjava.ioパッケージの入出力について説明します。

ストリームとは

ファイルなどの入出力を理解するには、ストリームの概念を理解する必要があります。

ストリーム概要図

I / Oストリームは、入力ソースまたは出力先を表します。
ストリームは、ディスクファイル、デバイス、その他のプログラム、メモリアレイなど、さまざまな種類のソースとデスティネーションを表すことができます。
ストリームは、単純なバイト、プリミティブデータ型、ローカライズされた文字、オブジェクトなど、さまざまな種類のデータをサポートします。
ストリームによっては単にデータを渡すだけです。他の人がデータを操作し、有用な方法で変換します。
内部的にどのように動作しても、すべてのストリームは、それらを使用するプログラムと同じシンプルモデルを提示します。
ストリームは一連のデータです。プログラムは入力ストリームを使用して、ソースからのデータを一度に1つずつ読み込みます。

https://docs.oracle.com/javase/tutorial/essential/io/streams.htmlより引用

引用元の最後の1文がとても大切です。概要図でディスクに格納されているファイルから文字列を読み込む場合、
ファイルの文字列データ全てをJVM上(メモリ上)に任意のオブジェクトに格納したら、サイズによっては簡単にOutOfMemmoryが発生します。

ストリームの機能としては、データソースの1部(1行とか1文字とかある一定のサイズ)ずつJVMに流し込むために存在します。(流量制御)

ストリームは、ファイル以外にもデバイス、メモリへのアクセスを提供します。

ストリームの分類

ストリームの種類

ストリームは、大きく入力ストリーム出力ストリームの2つに分類されます。
入力ストリームは、入力の名の通り、ファイルなどのデータソースを入力値としてデータを読み込むために提供されています。

対して、出力ストリームは、データをファイルなどのデータソースに出力するために提供されています。

入力ストリームと出力ストリームは、それぞれバイトストリーム文字ストリームに分類されます。

ストリームの種類 説明
バイトストリーム バイトストリームは、ストリームのデータを8ビットのバイト単位で入出力を行います
文字ストリーム Javaプラットフォーム内で全ての文字は、Unicode文字で取り扱いされています。
文字ストリームI / Oは、この内部フォーマットを
ローカル・キャラクタ・セット(UTF-8,SJISなど)との間で自動的に変換します。

バイトストリームと文字ストリームは、ストリームクラスの提供されるているメソッドを比較すると分かり易いでしょう。

バイトストリームであるFileInputStreamのread(byte[] b) メソッドがあります。
引数にしているのはbyte型の配列です。バイト単位で読み取る事が理解できます。

文字ストリームであるReaderread(char[] cbuf) メソッドがあります。
引数にしているのはchar型の配列です。単一文字charの配列で読み取る事が理解できます。

バイトストリームは、バイナリデータだけでなく勿論テキストデータも取り扱えます。
しかしながら、テキストデータを取り扱うには、文字ストリームを利用します。

これには、Javaの歴史的背景があります。
JDK1.0リリース時にはバイトストリームのjava.io.InputStream及びjava.io.OutPutStreamクラスは、元々ありました。
バイナリデータや文字データ関係なくファイルの入出力には、java.io.InputStream及びjava.io.OutPutStreamクラスを利用されていました。
JDK1.1リリース時に文字データを取り扱いし易くしたjava.io.Reader及びjava.io.Writerクラスがリリースされました。

これにより、文字列データは、java.io.Reader及びjava.io.Writerを利用し、
バイナリデータは、バイトストリームであるjava.io.InputStream及びjava.io.OutPutStreamで操作するのがお作法になっています。

更にバイナリデータのうち、画像データを取り扱う場合には、JDK1.4からjavax.imageioパッケージとそのクラスがリリースされました。

バッファの重要性

ストリーム概要図

入力ストリームにBufferedInputStream及びBufferedReaderが用意されており、
出力ストリームには、BufferedOutputStream及びBufferedWriterが用意されています。

Stream経由でファイルなどにアクセスする毎にソースからのデータを一度に1つずつ読む性質があると説明しました。

1つずつ読むことは、例えばハードウェアのディスク上のファイルに毎回アクセスすることを意味します。
これをDiskIOと呼び、ディスクアクセスのオーバヘッドがかかります。

このDiskIOの回数を減らす方法が、バッファを利用することです。

バッファとは、データを一時的に記憶する場所です。ファイルのデータの全て又は一部をバッファに読み取らせます。

BufferedInputStreamを利用したプログラムは、bufferに蓄えられたデータをstream経由で取得します。
これにより、BufferedInputStreamを利用しない方法より、DiskIOが減りオーバヘッドが抑えられ処理速度が早められます。

java.ioパッケージを利用して入出力を行う場合には、Buffered系のオブジェクトを使いましょう。