• java四种文件读写方式及性能比较


    测试代码

    package com.boot.demo.test.io;
    
    import java.io.*;
    import java.lang.reflect.Method;
    import java.nio.MappedByteBuffer;
    import java.nio.channels.FileChannel;
    import java.nio.file.Files;
    import java.nio.file.Paths;
    import java.nio.file.StandardOpenOption;
    import java.security.AccessController;
    import java.security.PrivilegedAction;
    
    /**
     * @author braska
     * @date 2020/3/19
     **/
    public class FileTest {
    
        public static void fileStream(String sourceFile, String targetFile) {
            File file = new File(targetFile);
            try (FileInputStream fis = new FileInputStream(sourceFile);
                 FileOutputStream fos = new FileOutputStream(file)) {
                byte[] bytes = new byte[1024 * 1024];
                int len;
                while ((len = fis.read(bytes)) > 0) {
                    fos.write(bytes, 0, len);
                }
            } catch (Exception e) {
    
            }
        }
    
        public static void bufferStream(String sourceFile, String targetFile) {
            try (BufferedInputStream bis = new BufferedInputStream(Files.newInputStream(Paths.get(sourceFile)));
                 BufferedOutputStream bos =
                         new BufferedOutputStream(Files.newOutputStream(Paths.get(targetFile),
                                 StandardOpenOption.CREATE,
                                 StandardOpenOption.TRUNCATE_EXISTING,
                                 StandardOpenOption.WRITE))) {
                byte[] bytes = new byte[1024 * 1024];
                int len;
                while ((len = bis.read(bytes)) > 0) {
                    bos.write(bytes, 0, len);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        public static void randomFile(String sourceFile, String targetFile) {
            try (RandomAccessFile read = new RandomAccessFile(sourceFile, "r");
                 RandomAccessFile write = new RandomAccessFile(targetFile, "rw")) {
                byte[] bytes = new byte[1024 * 1024];
                int len;
                while ((len = read.read(bytes)) > 0) {
                    write.write(bytes, 0, len);
                }
            } catch (Exception e) {
    
            }
        }
    
    
        public static void memoryMap(String sourceFile, String targetFile) {
            try (FileChannel rc = FileChannel.open(Paths.get(sourceFile));
                 FileChannel wc = FileChannel.open(Paths.get(targetFile),
                         StandardOpenOption.CREATE,
                         StandardOpenOption.READ,
                         StandardOpenOption.TRUNCATE_EXISTING,
                         StandardOpenOption.WRITE)) {
                long copy = 1L << 30;
                long cur = 0;
                long fileLength = rc.size();
                while (cur < fileLength) {
                    copy = cur + copy > fileLength ? (fileLength - cur) : copy;
                    MappedByteBuffer rMap = rc.map(FileChannel.MapMode.READ_ONLY, cur, copy);
                    MappedByteBuffer wMap = wc.map(FileChannel.MapMode.READ_WRITE, cur, copy);
                    for (int i = 0; i < copy; i++) {
                        byte b = rMap.get(i);			//从源文件读取字节
                        wMap.put(i, b);					//把字节写到目标文件中
                    }
                    System.gc();				//手动调用 GC		<必须的,否则出现异常>
                    System.runFinalization();
                    cur += copy;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        private static String buildFilePath(String path, String fileName, String extension) {
            return String.format("%s%s.%s", path, fileName, extension);
        }
    
        public static void main(String[] args) {
    /*        String path = "F:\workspace\demo\";
            String extension = "hprof";
            // 30M文件
            String sourceFile = buildFilePath(path, "01", extension);*/
    
    /*        String path = "E:\software\";
            String extension = "exe";
            // 460M文件
            String sourceFile = buildFilePath(path, "Anaconda3-2019.10-Windows-x86_64", extension);*/
    
            String path = "E:\software\";
            String extension = "zip";
            // 1.47G文件
            String sourceFile = buildFilePath(path, "software", extension);
            String targetFile;
            long start;
    
    /*        targetFile = buildFilePath(path, "target_file_stream", extension);
            start = System.currentTimeMillis();
            FileTest.fileStream(sourceFile, targetFile);
            System.out.println("file stream used time:" + (System.currentTimeMillis() - start));*/
    
    /*        targetFile = buildFilePath(path, "target_buffer_stream", extension);
            start = System.currentTimeMillis();
            FileTest.bufferStream(sourceFile, targetFile);
            System.out.println("buffer stream used time:" + (System.currentTimeMillis() - start));*/
    
    /*        targetFile = buildFilePath(path, "target_random_file", extension);
            start = System.currentTimeMillis();
            FileTest.randomFile(sourceFile, targetFile);
            System.out.println("random file used time:" + (System.currentTimeMillis() - start));*/
    
    
            targetFile = buildFilePath(path, "target_memory_map", extension);
            start = System.currentTimeMillis();
            FileTest.memoryMap(sourceFile, targetFile);
            System.out.println("memory map used time:" + (System.currentTimeMillis() - start));
        }
    }
    

      

    测试结果

    文件大小读写方式耗时
    30M 普通文件流 50-60 ms
    缓存流 32-35 ms
    随机文件方式 40-50 ms
    内存映射文件 50-60 ms
    461M 普通文件流 1300-2300 ms
    缓存流 1700-2000 ms
    随机文件方式 1300-3000 ms
    内存映射文件 890-1000 ms
    1.47G 普通文件流 11s
    缓存流 9s
    随机文件方式 10s
    内存映射文件 3s(首次较慢)

    结尾:测试1.47G大文件时,内存映射文件中copy大小做了调整,当copy为1G时(copy=1L<<30)性能最佳,整过过程1-3秒左右。调至128M、512M。大约耗时都在15秒左右。为了公平起见,把其他方法中的byte缓冲区大小也做了同样调整,耗时变化不大。有些甚至更慢。

    封装MappedBuffer工具类,代码如下:

    package com.boot.demo.test.io;
    
    import java.io.Closeable;
    import java.io.IOException;
    import java.nio.MappedByteBuffer;
    import java.nio.channels.FileChannel;
    import java.nio.file.Paths;
    import java.nio.file.StandardOpenOption;
    import java.util.concurrent.ArrayBlockingQueue;
    import java.util.concurrent.BlockingQueue;
    
    /**
     * @author braska
     * @date 2020/3/20
     **/
    public class MappedByteBufferReader implements Closeable {
    
        private static final int BUFFERED_SIZE = 1 << 27;
        private static final FileChannel.MapMode MAP_MODE = FileChannel.MapMode.READ_ONLY;
    
        private FileChannel fileChannel;
        private final long fileSize;
        private final int bufferedSize;
    
        BlockingQueue<MappedByteBuffer> queue;
    
        public MappedByteBufferReader(String file) throws Exception {
            this(file, BUFFERED_SIZE, MAP_MODE);
        }
    
        public MappedByteBufferReader(String file, int bufferedSize) throws Exception {
            this(file, bufferedSize, MAP_MODE);
        }
    
        public MappedByteBufferReader(String file, FileChannel.MapMode mapMode) throws Exception {
            this(file, BUFFERED_SIZE, mapMode);
        }
    
        public MappedByteBufferReader(String file, int bufferedSize, FileChannel.MapMode mapMode) throws Exception {
            this.fileChannel = FileChannel.open(Paths.get(file));
            this.fileSize = fileChannel.size();
            this.bufferedSize = bufferedSize;
            int capacity = (int) Math.ceil((double) fileSize / (double) bufferedSize);
            this.queue = new ArrayBlockingQueue(capacity);
            long readSize = bufferedSize;
            long cursor = 0l;
            while (cursor < fileSize) {
                readSize = cursor + readSize > fileSize ? fileSize - cursor : readSize;
                queue.add(
                        fileChannel.map(mapMode, cursor, readSize)
                );
                cursor += readSize;
            }
        }
    
        public byte[] read() {
            byte[] bytes;
            MappedByteBuffer byteBuffer = queue.poll();
            if (byteBuffer != null) {
                int limit = byteBuffer.limit();
                int position = byteBuffer.position();
    
                int realSize = this.bufferedSize;
                if (limit - position < this.bufferedSize) {
                    realSize = limit - position;
                }
                bytes = new byte[realSize];
                byteBuffer.get(bytes);
                byteBuffer.clear();
                return bytes;
            }
    
            return null;
        }
    
        public long size() {
            return this.fileSize;
        }
    
        @Override
        public void close() throws IOException {
            if (this.fileChannel != null) {
                this.fileChannel.close();
            }
        }
    
        public static void main(String[] args) {
            long start = System.currentTimeMillis();
            try (MappedByteBufferReader reader = new MappedByteBufferReader("E:\software\software.zip", 1 << 30);
                 FileChannel wc = FileChannel.open(Paths.get("E:\software\software_reader.zip"),
                         StandardOpenOption.CREATE,
                         StandardOpenOption.READ,
                         StandardOpenOption.TRUNCATE_EXISTING,
                         StandardOpenOption.WRITE)) {
                byte[] data;
                MappedByteBuffer writer = wc.map(FileChannel.MapMode.READ_WRITE, 0, reader.size());
                while ((data = reader.read()) != null) {
                    writer.put(data);
                }
                writer.clear();
                System.out.println("used times: " + (System.currentTimeMillis() - start));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
  • 相关阅读:
    MySQL 列子查询及 IN、ANY、SOME 和 ALL 操作符的使用
    MySQL 标量子查询
    MySQL 子查询(subquery)语法与用法实例
    如何上传本地音乐获取MP3外链(欢迎分享和转载)
    RabbitMQ与Kafka的区别及其简单原理实现
    MySQL中varchar和char的区别
    MySQL降低insert, update, delete的优先级来优化性能
    Eclipse代码自动提示
    Java生成XML文件
    Java读取XML文件
  • 原文地址:https://www.cnblogs.com/braska/p/12525773.html
Copyright © 2020-2023  润新知