专栏首页Apache IoTDBjava 字节流入门(内存数组流->文件流)

java 字节流入门(内存数组流->文件流)

文件系列往期文章:

java 字节流入门(文件流)

java 字节流入门(内存数组流)

本文介绍如何将内存数组流的数据写入文件流中。即将内存数组流中的数据通过文件流写到磁盘上,也叫flush,或持久化。毕竟内存是短暂的,磁盘才是永恒。

流就像管道,数据就像管道里的水。管道最大的魅力就是可以连接,使水从一个管道流到另一个管道,流也一样。

之前我们分别介绍了文件流和内存数组流,既然他们是流,那就应该可以连接起来。那么如何从内存数组流写入文件流呢?

java 字节流入门(文件流)中,我们介绍了 FileOutputStream(FOS) 和 RandomAccessFile(RAF) 两种写文件的方式。那么,当我们在内存中使用 ByteArrayOutputStream(BAOS) 维护数据时,如何利用 FOS 和 RAF 写文件呢,本文介绍四种方法。

准备工作:

   private static final Path path = Paths.get("src", "main", "resources", "test.myfile");
    private static final File file = path.toFile();
    private static int size = 1024*1024*800;
    private static byte[] b1 = new byte[size];
    private static ByteArrayOutputStream out = new ByteArrayOutputStream();

并将 b1 写入 out 中

out.write(b1);

writeTo写入FOS

首先,BAOS 有一个方法叫 writeTo(),这个方法可以将 BAOS 中的数据直接写入另一个字节输出流中。更准确的说法是,使用另一个字节输出流的 write() 方法将 BAOS 中的数据写出去。这里 BAOS 就和一个字节数组是等价的。

   /**
     * Writes the complete contents of this byte array output stream to
     * the specified output stream argument, as if by calling the output
     * stream's write method using <code>out.write(buf, 0, count)</code>.
     *
     * @param      out   the output stream to which to write the data.
     * @exception  IOException  if an I/O error occurs.
     */
    public synchronized void writeTo(OutputStream out) throws IOException {
        out.write(buf, 0, count);
    }

因为 FOS 本身就是 OutputStream,所以可以直接将 BAOS 中的数据通过 writeTo() 写入 FOS 中。

// 将 BAOS 中的数据写入 FileOutputStream
    private static void writeToFOS() throws IOException {
        if(file.exists())
            file.delete();
        // 将 ByteArrayOutputStream 缓存的数据写入 FileOutputStream 中,即写入文件中
        FileOutputStream fileOutputStream = new FileOutputStream(file, false);

        long time = System.currentTimeMillis();
        out.writeTo(fileOutputStream);
        fileOutputStream.close();
        time = System.currentTimeMillis() - time;
        System.out.println("将 "+ size + " 个字节写入 FOS 耗时:" + time + "ms");
        file.delete();
    }

writeTo写入RAF

由于 RandomAccessFile 不是标准的 OutputStream,所以没法直接用 writeTo() 方法实现。那如何将 BAOS 中的数据写入 RandomAccessFile 呢?

解决方案是:把 RandomAccessFile 包装成一个 OutputStream。我们实现一个 自定义的 OutputStream,继承 OutputStream,并用 RAF 的三种写方法覆盖 OutputStream 的原有写方法。

class MyRandomAccessFileOutputStream extends OutputStream {

    private RandomAccessFile raf;

    public MyRandomAccessFileOutputStream(RandomAccessFile raf) {
        this.raf = raf;
    }

    @Override
    public void write(int b) throws IOException {
        raf.write(b);
    }

    @Override
    public void write(byte b[]) throws IOException {
        raf.write(b);
    }

    @Override
    public void write(byte b[], int off, int len) throws IOException {
        raf.write(b, off, len);
    }

    public void seek(long pos) throws IOException {
        raf.seek(pos);
    }

    public long length() throws IOException {
        return raf.length();
    }

    @Override
    public void close() throws IOException {
        raf.close();
    }

}

接下来,就可以开心的把 RandomAccessFile 当 OutputStream 用了。

// 将 BAOS 中的数据写入 MyRandomAccessFileOutputStream
    private static void writeToMyRaf() throws IOException {
        if(file.exists())
            file.delete();
        RandomAccessFile raf = new RandomAccessFile(file, "rw");
        MyRandomAccessFileOutputStream myraf = new MyRandomAccessFileOutputStream(raf);

        long time = System.currentTimeMillis();
        out.writeTo(myraf);
        myraf.close();
        time = System.currentTimeMillis() - time;
        System.out.println("将 "+ size + " 个字节写入 MyRaf 耗时:" + time + "ms");
        file.delete();
    }

Copy写入FOS

大家记不记得 BAOS 还有个 toByteArray() 方法可以将其中的内容返回一个 byte 数组。其实现调用了 Arrays.copyOf() 方法,这里记做 copy 。

然后回想,其实各种流都有一个 write(byte[]) 方法,所以你们知道我想干嘛了吗,嗯,很粗暴的实现方式,上代码。

// 将 BAOS 中的数据 copy 写入 FileOutputStream
    private static void copyToFOS() throws IOException {
        if(file.exists())
            file.delete();
        // 将 ByteArrayOutputStream 缓存的数据写入 FileOutputStream 中,即写入文件中
        FileOutputStream fileOutputStream = new FileOutputStream(file, false);

        long time = System.currentTimeMillis();
        fileOutputStream.write(out.toByteArray());
        fileOutputStream.close();
        time = System.currentTimeMillis() - time;
        System.out.println("将 "+ size + " 个字节 copy 写入 FOS 耗时:" + time + "ms");
        file.delete();
    }

Copy写入RAF

同样的,RandomAccessFile 也有 write(byte[]) 方法,所以。。。继续上代码:

// 将 BAOS 中的数据 copy 写入 RandomAccessFile
    private static void copyToRaf() throws IOException {
        if(file.exists())
            file.delete();
        RandomAccessFile raf = new RandomAccessFile(file, "rw");

        long time = System.currentTimeMillis();
        raf.write(out.toByteArray());
        raf.close();
        time = System.currentTimeMillis() - time;
        System.out.println("将 "+ size + " 个字节 copy 写入 Raf 耗时:" + time + "ms");
        file.delete();
    }

接下来我们比较一下这四种方式的速度:

实验对比

写 800M数据。将 RAF 包装成 OutputStream 和 FileOutputStream 的效率差不多。对于两种文件流的写入方法,writeTo 总是比 copy 写入要快。毕竟 copy 多了一步拷贝,而且会占用额外内存。

所以不管哪种文件流,用 BAOS 的 writeTo() 都是最好的。

将 838860800 个字节写入 FOS 耗时:1413ms
将 838860800 个字节 copy 写入 FOS 耗时:2092ms
将 838860800 个字节写入 MyRaf 耗时:1452ms
将 838860800 个字节 copy 写入 Raf 耗时:2203ms

总结

假如用 ByteArrayOutputStream 管理内存数据,想持久化,那么有 FileOutputstream 和 RandomAccessFile 可以选择。

可以使用 BAOS 的 writeTo() 方法进行写入,为了将 RandomAccessFile 当流来用,可以外边包装一层。不要用 copy 方法,没好处的。

致谢:东哥、康博

代码:

https://github.com/qiaojialin/Java-IO-Learning

本文分享自微信公众号 - IoTDB漫游指南(Apache-IoTDB),作者:乔帮主

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-06-04

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Apache IoTDB 系列教程-7:时序数据文件格式 TsFile

    在大数据生态中有很多文件格式,像 Parquet,ORC,Avro 等等,都是针对嵌套数据设计的文件格式。这些文件格式普遍具有预先定义的schema,数据以行式...

    Apache IoTDB
  • Apache IoTDB 发布0.10.1!

    从上次0.10.0发布已经过去两个月了,这次给大家带来的0.10.1版本也是一个经过精心打磨修复多个bug的版本。这次更新中,文件结构和rpc没有任何变动,大家...

    Apache IoTDB
  • Apache IoTDB 随笔 - IoTDB核心技术剖析

    【摘要】Gartner指出赋能边缘是2020年十大战略技术趋势之一,5G加速IoT领域的发展,物联网设备数据的收集,存储和计算需求与日俱增。Apache IoT...

    Apache IoTDB
  • 一个小小的签到功能,到底用MySQL还是Redis?

    redis实现方案,使用bitmap来实现,bitmap是redis 2.2版本开始支持的功能,一般用于标识状态,

    良月柒
  • 一个小小的签到功能,到底用 MySQL 还是 Redis ? ?

    redis实现方案,使用bitmap来实现,bitmap是redis 2.2版本开始支持的功能,一般用于标识状态,

    芋道源码
  • 博客 | 机器学习中的数学基础(实战SVM)

    机器学习中的监督部分大多从样本数据开始,首先构建满足一定假设且逻辑合理、理论完备的“带参”假设函数

    AI研习社
  • 【极简版】SpringBoot+SpringData JPA 管理系统

    在上一篇中已经讲解了如何从零搭建一个SpringBoot+SpringData JPA的环境,测试接口的时候也成功获取得到数据了。

    Java3y
  • 一场微秒级的同步事故

    导读:诺兰导演作品《星际穿越》里面有这样一个片段,母舰损坏以后,处于高速旋转状态,库珀为了登上母舰,必须使自己的飞船也高速旋转, 与母舰同步成一样的旋转状态,才...

    glumes
  • 面试题: 了解OO的SOLID原则吗

    一个类只应承担一种责任。换句话说,让一个类只做一件事。如果需要承担更多的工作,那么分解这个类。

    用户1263954
  • 一周的闪念胶囊,总有一个能帮助到你

    1、不管是做需求还是测试,都应该考虑整个链路,确保兼容性或者其他模块不受影响。比如内容创作改动,应该考虑到审核侧、内容分发侧是否正常。

    公众号_松华说

扫码关注云+社区

领取腾讯云代金券