当前位置: 首页 > 面试题库 >

使用Java Nio将String写入文件的最佳方法

长孙阳焱
2023-03-14
问题内容

我需要使用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();
}

编辑:尝试了两个选项。以下是结果。

@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 ms

使用缓冲的Writer:占用563毫秒


问题答案:

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

  • 您真的需要琴弦吗?如果您从数据库存储和接收字节,则可以避免字符串分配和编码成本的总和。
  • 你真的需要rewindflip?似乎您正在为每个字符串创建一个新缓冲区,然后将其写入通道。(如果您采用NIO方式,则可以重用缓冲区而不是包装/丢弃的基准策略,我认为它们会做得更好)。
  • 请记住,wrap和allocateDirect可能会产生完全不同的缓冲区。两者都要进行基准测试,以权衡取舍。对于直接分配,请确保重用相同的缓冲区,以实现最佳性能。
  • 最重要的是:确保将NIO与BufferedOutputStream和/或BufferedWritter方法进行比较(也使用大小合适的中间件byte[]char[]缓冲区)。我见过很多,很多,很多人发现,NIO没有银弹。

如果您喜欢某些前沿技术,请返回IO
Trails
以获取一些NIO2:D。

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

干杯,

更新1:

由于@EJP提示我直接缓冲区对于解决此问题并不有效,因此我自己对其进行了基准测试,并最终使用内存映射文件开发了一个不错的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)而有所作弊。对于某些实际情况,这可能是不可能的。不过,您始终可以为操作使用“足够大的附加大小”,并在以后截断文件。

更新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);


 类似资料:
  • 问题内容: 使用Java在文件中间写入字节的最佳方法是什么? 问题答案: 在文件中间进行读写与在Java中使用一样简单。 尽管有它的名字,但它更像是和而不是像。它使您可以读取或查找文件,然后开始写您想停在的字节。 一旦发现此类,如果您对常规文件I / O有基本的了解,它将非常容易使用。 一个小例子:

  • 问题内容: 将File写入javax.servlet.ServletOutputStream的最有效方法是什么? 编辑: 如果使用NIO会更有效吗? 问题答案: 哪里是的FileInputStream和是。 IOUtils是Apache Commons中 Commons IO 模块的实用程序。

  • 问题内容: 我有一个程序,可以执行大量计算并经常将其报告到文件中。我知道频繁的写入操作会大大降低程序的速度,因此,为了避免出现这种情况,我希望有一个专门用于写入操作的线程。 现在,我正在用我写的这堂课(不耐烦的可以跳到问题的结尾): 然后我通过以下方式使用它: 尽管此方法运行良好,但我想知道: 是否有更好的方法来实现这一目标? 问题答案: 您的基本方法看起来不错。我将代码结构如下: 如果你不希望在

  • 问题内容: 我做了一个方法,需要一个和一个。它用该字符串作为内容的新文件替换该文件。 这就是我所做的: 但是,它非常缓慢。有时需要一分钟以上。 如何写出包含成千上万个字符的大文件? 问题答案: 确保分配了足够大的缓冲区: 您正在运行哪种操作系统?那也可以有很大的不同。但是,花一 分钟 时间写出一个小于大小的文件听起来像是系统问题。在Linux或其他* ix系统上,您可以使用类似的方法来查看JVM是

  • 问题内容: 如标题所示,我正在寻找最快的方式将整数数组写入文件。数组的大小将有所不同,并且实际上包含2500至25000000 int之间的任何位置。 这是我目前正在使用的代码: 鉴于DataOutputStream有一种写入字节数组的方法,我已经尝试将int数组转换为字节数组,如下所示: 像这样: 两者似乎都使速度略有提高,约为5%。我没有对它们进行足够严格的测试以确认这一点。 是否有任何技术可

  • 我想使用jackson将ArrayList转换为JSONArray。 event.java:这是一个java bean类,有两个字段“field1”和“field2”映射为JSONProperty。 转换 至 我能想到的方法是:writeListToJsonArray():