web-dev-qa-db-ja.com

Java nioを使用して文字列をファイルに書き込む最良の方法

Java nio。エンコーディングはISO-8859-1です。

現在、次のように書いています。同じことを行うbetter方法はありますか?

public void writeToFile(Long limit) throws IOException{
     String fileName = "/xyz/test.txt";
     File file = new File(fileName);        
     FileOutputStream fileOutputStream = new FileOutputStream(file, true);  
     FileChannel fileChannel = fileOutputStream.getChannel();
     ByteBuffer byteBuffer = null;
     String messageToWrite = null;
     for(int i=1; i<limit; i++){
         //messageToWrite = get String Data From database
         byteBuffer = ByteBuffer.wrap(messageToWrite.getBytes(Charset.forName("ISO-8859-1")));
         fileChannel.write(byteBuffer);         
     }
     fileChannel.close();
}

編集:両方のオプションを試しました。結果は次のとおりです。

@Test
public void testWritingStringToFile() {
    DiagnosticLogControlManagerImpl diagnosticLogControlManagerImpl = new DiagnosticLogControlManagerImpl();
    try {
        File file = diagnosticLogControlManagerImpl.createFile();
        long startTime = System.currentTimeMillis();
        writeToFileNIOWay(file);
        //writeToFileIOWay(file);
        long endTime = System.currentTimeMillis();
        System.out.println("Total Time is  " + (endTime - startTime));
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

/**
 *
 * @param limit
 *            Long
 * @throws IOException
 *             IOException
 */
public void writeToFileNIOWay(File file) throws IOException {
    FileOutputStream fileOutputStream = new FileOutputStream(file, true);
    FileChannel fileChannel = fileOutputStream.getChannel();
    ByteBuffer byteBuffer = null;
    String messageToWrite = null;
    for (int i = 1; i < 1000000; i++) {
        messageToWrite = "This is a test üüüüüüööööö";
        byteBuffer = ByteBuffer.wrap(messageToWrite.getBytes(Charset
            .forName("ISO-8859-1")));
        fileChannel.write(byteBuffer);
    }
}

/**
 *
 * @param limit
 *            Long
 * @throws IOException
 *             IOException
 */
public void writeToFileIOWay(File file) throws IOException {
    FileOutputStream fileOutputStream = new FileOutputStream(file, true);
    BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(
        fileOutputStream, 128 * 100);
    String messageToWrite = null;
    for (int i = 1; i < 1000000; i++) {
        messageToWrite = "This is a test üüüüüüööööö";
        bufferedOutputStream.write(messageToWrite.getBytes(Charset
            .forName("ISO-8859-1")));
    }
    bufferedOutputStream.flush();
    fileOutputStream.close();
}

private File createFile() throws IOException {
    File file = new File(FILE_PATH + "test_sixth_one.txt");
    file.createNewFile();
    return file;
}

ByteBufferとChannelの使用:4402ミリ秒かかりました

バッファーライターを使用:563ミリ秒かかった

37
nobody

ソフトウェアのベンチマークを行わなくても、厳密な答えを得ることができるとは思いません。 NIOは、適切な条件下でアプリケーションを大幅に高速化する可能性がありますが、速度が低下する可能性もあります。以下にいくつかのポイントを示します。

  • 本当に文字列が必要ですか?データベースからバイトを保存および受信する場合、文字列の割り当てとエンコードのコストをすべて回避できます。
  • 本当にrewindflipが必要ですか?すべての文字列に新しいバッファを作成し、それをチャネルに書き込むだけのようです。 (NIOの方法で、ラッピング/破棄の代わりにバッファを再利用するベンチマーク戦略があれば、より良くなると思います)。
  • wrapallocateDirect はまったく異なるバッファーを生成する可能性があることに注意してください。トレードオフを把握するための両方のベンチマーク。直接割り当てでは、最高のパフォーマンスを実現するために、同じバッファを必ず再利用してください。
  • そして最も重要なことは、NIOを BufferedOutputStream および/または BufferedWritter アプローチと比較することです(中間byte[]またはchar[]同様に適切なサイズのバッファ)。 多く多く多く NIOが特効薬ではないことを発見した人々を見てきました。

いくつかの出血するエッジが好きなら... IO Trails に戻って、いくつかのNIO2について:D。

そして、ここに 異なる戦略を使用したファイルコピーに関する興味深いベンチマーク があります。私はそれが別の問題であることを知っていますが、ほとんどの事実と著者の結論はあなたの問題にも当てはまると思います。

乾杯、

更新1:

@EJPは、この問題に対してダイレクトバッファーは効率的ではないことを教えてくれたので、自分でベンチマークを行い、最終的にネモリーマップファイルを使用したNice NIOソリューションを作成しました。 OS X Lionを実行している私のMacbookでは、これはBufferedOutputStreamを確実なマージンで破りました。ただし、これはOS /ハードウェア/ VM固有:

public void writeToFileNIOWay2(File file) throws IOException {
    final int numberOfIterations = 1000000;
    final String messageToWrite = "This is a test üüüüüüööööö";
    final byte[] messageBytes = messageToWrite.
            getBytes(Charset.forName("ISO-8859-1"));
    final long appendSize = numberOfIterations * messageBytes.length;
    final RandomAccessFile raf = new RandomAccessFile(file, "rw");
    raf.seek(raf.length());
    final FileChannel fc = raf.getChannel();
    final MappedByteBuffer mbf = fc.map(FileChannel.MapMode.READ_WRITE, fc.
            position(), appendSize);
    fc.close();
    for (int i = 1; i < numberOfIterations; i++) {
        mbf.put(messageBytes);
    }
} 

事前に追加する合計サイズ(約26 MB)を計算することで少しcheしたことは認めます。これは、いくつかの実際のシナリオでは不可能な場合があります。それでも、操作には常に「十分な大きさの追加サイズを使用し、後でファイルを切り捨てることができます。

更新2(2019):

問題に対する最新の(Java 11+)ソリューションなど)をお探しの場合は、 @ DodgyCodeExceptionのアドバイス を使用して Java.nio.file.Files.writeString

String fileName = "/xyz/test.txt";
String messageToWrite = "My long string";
Files.writeString(Paths.get(fileName), messageToWrite, StandardCharsets.ISO_8859_1);
15
Anthony Accioly

Java nioを使用したone-lineソリューションがあります。

Java.nio.file.Files.write(Paths.get(file.toURI()), 
                          "My string to save".getBytes("utf-8"),
                          StandardOpenOption.CREATE,
                          StandardOpenOption.TRUNCATE_EXISTING);

このソリューションを他のソリューションと比較したことはありませんが、組み込みファイルのオープン/ライト/クローズファイルの使用は高速で、コードは非常に小さいはずです。

54
Roberto

FileWriterの周りのBufferedWriterは、思いつくすべてのNIOスキームよりもほぼ確実に高速になります。あなたのコードは確かに最適ではありません。書き込みごとに新しいByteBufferを使用し、範囲外になりそうなときに無意味な操作を行いますが、いずれにせよあなたの質問は誤解に基づいています。 NIOは、FileChannel.transferTo/From()を使用していない限り、「メモリフットプリントをOSにオフロード」しません。これは、このインスタンスでは使用できません。

NBは例外であるため、コメントで提案されているPrintWriterは使用しません。 PWは実際には、気にしないコンソールとログファイル専用です。

8
user207421

これは短くて簡単な方法です。ファイルを作成し、コードプロジェクトに関連するデータを書き込みます。

private void writeToFile(String filename, String data) {
    Path p = Paths.get(".", filename);
    try (OutputStream os = new BufferedOutputStream(
        Files.newOutputStream(p, StandardOpenOption.CREATE, StandardOpenOption.APPEND))) {
        os.write(data.getBytes(), 0, data.length());
    } catch (IOException e) {
        e.printStackTrace();
    }
}
0
darkhorse21