使用java nio将String写入文件的最佳方法

nob*_*ody 37 java file-io nio character-encoding

我需要使用java nio将大字符串写入(追加)平面文件.编码为ISO-8859-1.

目前我们写的如下所示.有没有更好的方法来做同样的事情?

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();
}
Run Code Online (Sandbox Code Playgroud)

编辑:尝试了两种选择.以下是结果.

@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;
}
Run Code Online (Sandbox Code Playgroud)

使用ByteBuffer和Channel:耗时4402 ms

使用缓冲的Writer:耗时563 ms

Rob*_*rto 55

使用Java nio 有一个单行解决方案:

java.nio.file.Files.write(Paths.get(file.toURI()), 
                          "My string to save".getBytes("utf-8"),
                          StandardOpenOption.CREATE,
                          StandardOpenOption.TRUNCATE_EXISTING);
Run Code Online (Sandbox Code Playgroud)

我没有将此解决方案与其他解决方案进行基准测试,但是使用open-write-close文件的内置实现应该很快并且代码非常小.


Ant*_*oly 16

如果不对您的软件进行基准测试,我认为您无法得到严格的答案.NIO可能会在适当的条件下显着加快应用程序的速度,但也可能使事情变得更慢.以下是一些观点:

  • 你真的需要弦乐吗?如果存储和接收来自数据库的字节,则可以避免字符串分配和编码成本.
  • 你真的需要rewindflip?好像你正在为每个字符串创建一个新缓冲区,只是将它写入通道.(如果你采用NIO方式,重复使用缓冲区而不是包装/丢弃的基准策略,我认为它们会做得更好).
  • 请记住,wrapallocateDirect可能产生完全不同的缓冲区.基准两者都是为了掌握权衡取舍.通过直接分配,请务必重用相同的缓冲区以获得最佳性能.
  • 最重要的是:确保将NIO与BufferedOutputStream和/或BufferedWritter方法进行比较(使用具有合理大小的中间byte[]char[]缓冲区).我见过很多,很多,很多人发现,NIO没有银弹.

如果你喜欢一些前沿...回到IO Trails寻找一些NIO2:D.

这是一个关于使用不同策略进行文件复制有趣基准.我知道这是一个不同的问题,但我认为大多数事实和作者的结论也适用于你的问题.

干杯,

更新:

由于@EJP告诉我直接缓冲区不能有效解决这个问题,我自己进行基准测试,最后得到了一个使用nemory-mapped文件的好的NIO解决方案.在我运行OS X Lion的Macbook中,这个节拍BufferedOutputStream有很大的优势.但请记住,这可能是OS/Hardware/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);
    }
} 
Run Code Online (Sandbox Code Playgroud)

我承认我通过计算要追加的总大小(大约26 MB)来作弊.对于几个真实场景,这可能是不可能的.尽管如此,您仍然可以使用"足够大的附加大小进行操作,然后截断文件.


use*_*421 8

围绕FileWriter的BufferedWriter几乎肯定会比你能想出的任何NIO方案更快.你的代码当然不是最优的,每次写入都有一个新的ByteBuffer,然后当它即将超出范围时对它进行无意义的操作,但无论如何你的问题都是基于误解.除非您使用FileChannel.transferTo/From(),否则NIO根本不会"将内存占用量减少到操作系统",在这种情况下您无法使用.

NB不要像评论中建议的那样使用PrintWriter,因为这会吞下异常.PW实际上只适用于您不关心的控制台和日志文件.