Java 写入文件的最快方式?

Java 写入文件的最快方式?,java,file-io,Java,File Io,我创建了一个方法,它接受一个文件和一个字符串。它将用一个新文件替换该文件,并将该字符串作为其内容 这是我做的: public static void Save(File file, String textToSave) { file.delete(); try { BufferedWriter out = new BufferedWriter(new FileWriter(file)); out.write(textToSave);

我创建了一个方法,它接受一个
文件
和一个
字符串
。它将用一个新文件替换该文件,并将该字符串作为其内容

这是我做的:

public static void Save(File file, String textToSave) {

    file.delete();
    try {
        BufferedWriter out = new BufferedWriter(new FileWriter(file));
        out.write(textToSave);
        out.close();
    } catch (IOException e) {
    }
}
然而,这是痛苦的缓慢。有时需要一分钟


如何编写包含数万到一百万个字符的大型文件?

您可以了解Java的NIO功能。它可能支持您想要做的事情


确保分配足够大的缓冲区:

BufferedWriter out = new BufferedWriter(new FileWriter(file), 32768);
您正在运行什么类型的操作系统?这也会有很大的不同。然而,花一分钟写出一个不太大的文件听起来像是一个系统问题。在Linux或其他*ix系统上,您可以使用
strace
之类的工具来查看JVM是否正在进行大量不必要的系统调用。(很久以前,Java I/O相当愚蠢,如果你不小心的话,它会进行大量的低级
write()
系统调用,但当我说“很久以前”时,我指的是1998年左右。)


请注意,Java程序以简单的方式编写简单文件,但速度非常慢,这种情况本质上是一种奇怪的情况。你能告诉我在写文件时CPU是否负载过重吗?不应该这样;这样的事情几乎没有CPU负载

在Java中,BufferWriter非常慢:直接使用本机方法,并尽可能少地调用它们(每次调用时尽可能多地提供数据)

此外,删除文件可能需要一段时间(可能是先将其复制到回收站)。只需覆盖文件,就像上面的代码一样。

一个简单的测试

char[] chars = new char[100*1024*1024];
Arrays.fill(chars, 'A');
String text = new String(chars);
long start = System.nanoTime();
BufferedWriter bw = new BufferedWriter(new FileWriter("/tmp/a.txt"));
bw.write(text);
bw.close();
long time = System.nanoTime() - start;
System.out.println("Wrote " + chars.length*1000L/time+" MB/s.");
印刷品

Wrote 135 MB/s.

尝试使用内存映射文件:

FileChannel rwChannel = new RandomAccessFile("textfile.txt", "rw").getChannel();
ByteBuffer wrBuf = rwChannel.map(FileChannel.MapMode.READ_WRITE, 0, textToSave.length());

wrBuf.put(textToSave.getBytes());

rwChannel.close();

您好,我创建了两种创建大文件的方法,在windows 7上运行程序,64位,8GB RAM机器,JDK 8及以下是结果。
在这两种情况下,都创建了180 MB的文件,每行包含1到2000万个数字(在印度系统中为200万)

Java程序内存逐渐增长到600 MB

第一输出

Approach = approach-1 (Using FileWriter)
Completed file writing in milli seconds = 4521 milli seconds.
第二输出

Approach = approach-2 (Using FileChannel and ByteBuffer)
Completed file writing in milli seconds = 3590 milli seconds.
一个观察结果-我在方法2中计算位置(pos变量),如果我注释掉它,则由于位置被覆盖,只有最后一个字符串可见,但时间缩短到近2000毫秒

附加代码

import java.io.FileWriter;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.concurrent.TimeUnit;

public class TestLargeFile {

    public static void main(String[] args) {
        writeBigFile();
    }

    private static void writeBigFile() {
        System.out.println("--------writeBigFile-----------");
        long nanoTime = System.nanoTime();
        String fn = "big-file.txt";
        boolean approach1 = false;
        System.out.println("Approach = " + (approach1 ? "approach-1" : "approach-2"));
        int numLines = 20_000_000;
        try {
            if (approach1) {
                //Approach 1 -- for 2 crore lines takes 4.5 seconds with 180 mb file size
                approach1(fn, numLines);
            } else {
                //Approach 2 -- for 2 crore lines takes nearly 2 to 2.5 seconds with 180 mb file size
                approach2(fn, numLines);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        System.out.println("Completed file writing in milli seconds = " + TimeUnit.MILLISECONDS.convert((System.nanoTime() - nanoTime), TimeUnit.NANOSECONDS));
    }

    private static void approach2(String fn, int numLines) throws IOException {
        StringBuilder sb = new StringBuilder();
        FileChannel rwChannel = new RandomAccessFile(fn, "rw").getChannel();
        ByteBuffer wrBuf;

        int pos = 0;
        for (int i = 1; i <= numLines; i++) {
            sb.append(i).append(System.lineSeparator());
            if (i % 100000 == 0) {
                wrBuf = rwChannel.map(FileChannel.MapMode.READ_WRITE, pos, sb.length());
                pos += sb.length();
                wrBuf.put(sb.toString().getBytes());
                sb = new StringBuilder();
            }
        }
        if (sb.length() > 0) {
            wrBuf = rwChannel.map(FileChannel.MapMode.READ_WRITE, pos, sb.length());
            wrBuf.put(sb.toString().getBytes());
        }
        rwChannel.close();
    }

    private static void approach1(String fn, int numLines) throws IOException {
        StringBuilder sb = new StringBuilder();
        for (int i = 1; i <= numLines; i++) {
            sb.append(i).append(System.lineSeparator());
        }
        FileWriter fileWriter = new FileWriter(fn);
        fileWriter.write(sb.toString());
        fileWriter.flush();
        fileWriter.close();
    }
}
导入java.io.FileWriter;
导入java.io.IOException;
导入java.io.RandomAccessFile;
导入java.nio.ByteBuffer;
导入java.nio.channels.FileChannel;
导入java.util.concurrent.TimeUnit;
公共类TestLargeFile{
公共静态void main(字符串[]args){
writeBigFile();
}
私有静态void writeBigFile(){
System.out.println(“--writeBigFile------”;
long nanoTime=System.nanoTime();
String fn=“big file.txt”;
布尔逼近1=假;
系统输出打印LN(“进近=”+(进近1?“进近1”:“进近2”);
int numLines=20_000_000;
试一试{
如果(方法1){
//方法1——对于2百万行,文件大小为180 mb时需要4.5秒
方法1(fn、numLines);
}否则{
//方法2——对于2百万行,文件大小为180 mb时需要将近2到2.5秒
方法2(fn、numLines);
}
}捕获(IOE异常){
e、 printStackTrace();
}
System.out.println(“以毫秒为单位完成文件写入=“+TimeUnit.millizes.convert((System.nanoTime()-nanoTime),TimeUnit.NANOSECONDS));
}
私有静态void方法2(字符串fn,int numLines)引发IOException{
StringBuilder sb=新的StringBuilder();
FileChannel rwChannel=新的随机访问文件(fn,“rw”).getChannel();
比特布弗;
int pos=0;
对于(int i=1;i 0){
wrBuf=rwChannel.map(FileChannel.MapMode.READ_-WRITE,pos,sb.length());
wrBuf.put(sb.toString().getBytes());
}
rwChannel.close();
}
私有静态void方法1(字符串fn,int numLines)引发IOException{
StringBuilder sb=新的StringBuilder();

对于(int i=1),我删除文件是不必要的。你正在重写。多少时间是CPU时间和多少I/O(“系统”)时间?对于大型文件,创建巨大的<代码> TetrToStuave字符串可能占主导地位。与你的问题没有直接关系:你可以考虑重构Out.Couter()。语句,以便在finally块中执行。如果写入时抛出错误,它仍将关闭。不要忽略IOexception,这可能导致程序以神秘的方式失败。与在写入之前删除文件或直接重写文件相比,我建议将其写入临时文件,然后在旧文件上重命名它这意味着,如果IO中途失败,您不会冒险用损坏的文件替换旧文件。同意。他甚至可以提前知道所需的缓冲区大小,因为他将字符串作为param:textToSave.getBytes().length@RockyMadden yea这是一个非常好的观点。但是,通过Java IO库转储字符串应该非常快,几乎任何方式都可以。getBytes()调整缓冲区可能非常昂贵。我建议您只使用256K,不要担心。-1因为如果您正在编写一个巨大的字符串,您甚至不需要字符缓冲区-您可以直接将其传递给FileWriter,它将在单个批处理中处理。在字节级别使用缓冲区可能是值得的(使用OutputStreamWriter+BufferedOutputStream+FileOutputStream),因为字符编码是用一个缓冲区完成的,缓冲区的大小你无法控制,我相信它很小。但不是在字符级别。答案很好。结果表明,它写得这么慢的原因实际上不是因为写方法,而是因为我使用了这么长的
字符串
import java.io.FileWriter;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.concurrent.TimeUnit;

public class TestLargeFile {

    public static void main(String[] args) {
        writeBigFile();
    }

    private static void writeBigFile() {
        System.out.println("--------writeBigFile-----------");
        long nanoTime = System.nanoTime();
        String fn = "big-file.txt";
        boolean approach1 = false;
        System.out.println("Approach = " + (approach1 ? "approach-1" : "approach-2"));
        int numLines = 20_000_000;
        try {
            if (approach1) {
                //Approach 1 -- for 2 crore lines takes 4.5 seconds with 180 mb file size
                approach1(fn, numLines);
            } else {
                //Approach 2 -- for 2 crore lines takes nearly 2 to 2.5 seconds with 180 mb file size
                approach2(fn, numLines);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        System.out.println("Completed file writing in milli seconds = " + TimeUnit.MILLISECONDS.convert((System.nanoTime() - nanoTime), TimeUnit.NANOSECONDS));
    }

    private static void approach2(String fn, int numLines) throws IOException {
        StringBuilder sb = new StringBuilder();
        FileChannel rwChannel = new RandomAccessFile(fn, "rw").getChannel();
        ByteBuffer wrBuf;

        int pos = 0;
        for (int i = 1; i <= numLines; i++) {
            sb.append(i).append(System.lineSeparator());
            if (i % 100000 == 0) {
                wrBuf = rwChannel.map(FileChannel.MapMode.READ_WRITE, pos, sb.length());
                pos += sb.length();
                wrBuf.put(sb.toString().getBytes());
                sb = new StringBuilder();
            }
        }
        if (sb.length() > 0) {
            wrBuf = rwChannel.map(FileChannel.MapMode.READ_WRITE, pos, sb.length());
            wrBuf.put(sb.toString().getBytes());
        }
        rwChannel.close();
    }

    private static void approach1(String fn, int numLines) throws IOException {
        StringBuilder sb = new StringBuilder();
        for (int i = 1; i <= numLines; i++) {
            sb.append(i).append(System.lineSeparator());
        }
        FileWriter fileWriter = new FileWriter(fn);
        fileWriter.write(sb.toString());
        fileWriter.flush();
        fileWriter.close();
    }
}