【问题标题】:Is put-ing to a ByteBuffer then writing it to a file more efficient than writing the individual field放入 ByteBuffer 然后将其写入文件比写入单个字段更有效
【发布时间】:2014-07-09 05:52:57
【问题描述】:

我想ONLY将对象的数据成员的值写入文件,所以这里我不能使用序列化,因为它写了很多我不需要的其他信息。这是我以两种方式实现的。一个使用字节缓冲区,另一个不使用它。

不使用 ByteBuffer: 第一种方法

public class DemoSecond {

    byte characterData;
    byte shortData;
    byte[] integerData;
    byte[] stringData;

    public DemoSecond(byte characterData, byte shortData, byte[] integerData,
        byte[] stringData) {
        super();
        this.characterData = characterData;
        this.shortData = shortData;
        this.integerData = integerData;
        this.stringData = stringData;
    }

    public static void main(String[] args) {
        DemoSecond dClass= new DemoSecond((byte)'c', (byte)0x7, new byte[]{3,4},
            new byte[]{(byte)'p',(byte)'e',(byte)'n'});

        File checking= new File("c:/objectByteArray.dat");
        try {
            if (!checking.exists()) {
                checking.createNewFile();
            }
            // POINT A
            FileOutputStream bo = new FileOutputStream(checking);
            bo.write(dClass.characterData);
            bo.write(dClass.shortData);
            bo.write(dClass.integerData);
            bo.write(dClass.stringData);
            // POINT B
            bo.close();
        } catch (FileNotFoundException e) {
                System.out.println("FNF");
                e.printStackTrace();
        } catch (IOException e) {
                System.out.println("IOE");
                e.printStackTrace();
        }
    }
}

使用字节缓冲区:还有一件事是数据成员的大小将始终保持固定,即 characterData= 1byte、shortData= 1byte、integerData= 2byte 和 stringData= 3byte。所以这个类的总大小是 7byte ALWAYS

第二种方法

            // POINT A
            FileOutputStream bo = new FileOutputStream(checking);
            ByteBuffer buff= ByteBuffer.allocate(7);
            buff.put(dClass.characterData);
            buff.put(dClass.shortData);
            buff.put(dClass.integerData);
            buff.put(dClass.stringData);
            bo.write(buff.array());
            // POINT B

我想知道这两种方法中哪一种更优化?并请说明原因。

上面的类DemoSecond只是一个示例类。

我的原始类将是大小为 5 到 50 个字节。我不认为这里的大小可能是问题。 但是我的每个班级都是固定大小的,例如 DemoSecond

还有很多这种类型的文件,我要写入二进制文件。

附言

如果我使用序列化,它还会写入单词“characterData”、“shortData”、“integerData”、“stringData”以及我不想在文件中写入的其他信息。我在这里担心的是仅限他们的价值观。在这个例子中是:'c', 7, 3,4'p','e','n'。我只想将这 7 个字节写入文件,而不是其他对我无用的信息。

【问题讨论】:

  • 为什么不声明transient所有不必要的部分?
  • 如果我使用序列化,它也会写入单词“characterData”、“shortData”、“integerData”、“stringData”。我在这里担心的只是价值观。在本例中:'c', 7, 3,4'p','e','n'
  • 你为什么不在你的代码中加入一些计时,并用一百万个对象来测试它。我的猜测是非缓冲输出流是最快的。

标签: java java-io bytebuffer


【解决方案1】:

在执行文件 I/O 时,您应该记住,I/O 操作可能比 CPU 在输出代码中完成的任何工作要慢得多。大致而言,I/O 的成本与您正在写入的数据量成正比,再加上执行 I/O 的每个操作系统调用的固定成本。

因此,在您的情况下,您希望最小化操作系统调用的数量来进行写入。这是通过在应用程序中缓冲数据来完成的,因此应用程序执行较少的较大操作系统调用。

正如您所做的那样,使用字节缓冲区是执行此操作的一种方法,因此您的 ByteBuffer 代码将比您的 FileOutputStream 代码更有效。

但还有其他考虑因素。您的示例没有执行很多写入。所以无论如何它可能会非常快。任何优化都可能是过早的优化。优化往往会使代码更复杂、更难理解。要了解您的ByteBuffer 代码,读者需要了解ByteBuffer 的工作原理以及他们需要了解FileOutputStream 代码的所有内容。而且,如果您更改了文件格式,则更有可能在 ByteBuffer 代码中引入错误(例如,缓冲区太小)。

通常会进行输出缓冲。因此,Java 已经提供了帮助您的代码,您应该不会感到惊讶。该代码将由专家编写、测试和调试。除非您有特殊要求,否则您应该始终使用此类代码,而不是编写自己的代码。我指的代码是BufferedOutputStream 类。

要使用它,只需修改不使用 ByteBuffer 的代码,方法是将打开文件的代码行更改为

 OutputStream bo = new BufferedOutputStream(new FileOutputStream(checking));

【讨论】:

  • 同意 BufferedOutputStream 解决此类问题。缓冲写入是大多数性能优势的来源。我还可以使用 DataOutputStream 作为链中的最后一个流。
  • 如果我使用 OutputStream bo = new BufferedOutputStream(new FileOutputStream(checking)); 那么第一种方法(没有字节缓冲区)的执行时间更少,你是说我应该使用输出流的第二种方法吗?
  • @kaze 不,根本不要使用BYteBuffer
  • 这样的 OutputStream bo = new BufferedOutputStream(new FileOutputStream(checking)); bo.write(dClass.characterData); bo.write(dClass.shortData); bo.write(dClass.integerData); bo.write(dClass.stringData);
【解决方案2】:

这两种方法的区别仅在于分配的字节缓冲区。

如果您担心对文件进行不必要的写入操作,那么已经有一个 BufferedOutputStream 可以使用,内部为其分配缓冲区,如果您多次写入同一个输出流,它肯定比分配更有效每次手动缓冲。

【讨论】:

    【解决方案3】:

    最简单的方法是在 FileOutputStream 周围的 BufferedOutputStream 周围使用 DataOutputStream。

    NB 您不能将“shortData”压缩到一个字节中。使用DataOutputStream的各种原语,回读时使用DataInputStream对应的原语。

    【讨论】:

    • 上面的类DemoSecond只是一个示例类。即使 short 的大小为 2 个字节,我也会将其转换为 2 个字节,然后一个接一个地分别写入这两个字节。例如。短 a= 1282 (1010000010),所以我的两个字节将是 5,2
    • 更有理由使用 DataOutputStream 为您完成所有这些工作。
    猜你喜欢
    • 1970-01-01
    • 2019-07-24
    • 2011-12-06
    • 2014-07-03
    • 2014-06-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多