Java二进制IO写入和读取

问题描述:

我有这种结构,我想要读取和写入文件,并且我想尽可能以最快的方式来完成。Java二进制IO写入和读取

class Map 
{ 
    String name; 
    int tiles[][]; 
} 

这样做的最好方法是什么?我主要是一名C++程序员,我不知道在Java中这样做的最佳方法。它似乎应该很简单,但我不知道如何在Java中执行二进制io。

这是我到目前为止创建:

void Write(String fileName) 
{ 
    final ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(fileName))); 

    oos.writeUTF(name); 
    oos.writeInt(tiles.length); 
    oos.writeInt(tiles[0].length); 
    for(int i = 0; i < tiles.length; i++) 
    for(int j = 0; j < tiles[0].length; j++) 
     oos.writeInt(tiles[i][j]); 

    oos.close(); 
} 

void Read(String fileName) 
{ 
    final ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(new FileInputStream(file))); 

    name = ois.readUTF(); 

    int w = ois.readInt(); 
    int h = ois.readInt(); 

    tiles = new int[h][w]; 

    for(int i = 0; i < h; i++) 
    for(int j = 0; j < w; j++) 
     tiles[i][j] = ois.readInt(); 

    ois.close(); 
} 

这是一样快,我能得到什么?

+0

注意到应该是存在一个很普遍的使用'java.util中。地图'类。您希望将您的'Map'类重命名为不同的名称,以避免名称冲突和其他开发人员混淆(重新)查看您的代码。 – BalusC 2010-09-11 20:13:29

http://download.oracle.com/javase/6/docs/api/java/io/ObjectOutputStream.html

// change to class to support serialization 
class Map implements Serializable 
{ 
    String name; 
    int tiles[][]; 
} 

代码片段来写对象

FileOutputStream fos = new FileOutputStream("t.tmp"); 
ObjectOutputStream oos = new ObjectOutputStream(fos); 

Map m = new Map(); 

// set Map properties ... 

oos.writeObject(m); 
oos.close(); 
+1

'Map'应该实现'Serializable'。并给出一个更新版本的文档的链接。 – Bozho 2010-09-11 19:47:54

+0

类Map需要实现接口Serializable才能正常工作 – 2010-09-11 19:48:43

如果您正在寻找在Java中执行I/O的最快方法,请查看nio(未缓冲的io)类。我相当相信你也可以做二进制I/O。

http://download.oracle.com/javase/1.4.2/docs/api/java/nio/package-summary.html

如果你想要做的是写一个和唯一的结构,那么你应该手工编写系列化和反序列化。我建议写一个count,然后是字符串字符,然后是数组的维数,然后是所有整数。你将不得不担心自己的字节顺序,每个字符占两个字节,每个int占四个字节。

如果您需要速度,请不要使用Java序列化,并且不要仅将NIO用于单线程磁盘文件I/O情况。请使用缓冲流。

您确定这确实是一个性能至关重要的操作吗?无论如何,这个阵列有多大?

您是否考虑过NIO的内存映射功能?现在你正在让内核完成繁重的工作。在任何情况下制作很多小文件都可能会让你胃口大开。请注意,我区分了可以使用NIO执行的两件事:您可以使用通道和缓冲区来简单读写。我很怀疑只读一个文件中的数据会带来性能上的好处。或者,您可以存储映射文件,并让内核分页输入和输出数据。 可能适合您,取决于数据总量和涉及的内存配置。

+0

读入文件将是时间关键的。每个文件将成为整个地图的一部分。所以每张地图的尺寸​​可能只有30x30到50x50,但随着角色的移动,我将不得不在飞行中阅读它们。 – 2010-09-11 19:53:00

+0

@gamernb也许你应该问你的一般做法是否好。 :)你可以在内存或缓存中存储很多30x30或50x50。 – InsertNickHere 2010-09-11 20:44:59

我有一个非常具体的技术,我用这种东西。这是一种混合方法,我可以在最高性能的基本io代码中找到结果,但仍保持可读性和与简单Java序列化的兼容性。

在Java序列化中使用的反射是历史上认为很慢并且速度很慢的部分。但自从sun.misc.Unsafe加入以来,这部分实际上非常快。仍然有第一次调用clazz.getDeclaredFields()和java.lang.Class的其他'getDeclared'类型方法的初始命中,但是这些方法在VM级别被缓存,所以在第一个(非常明显的)击中。

Java序列化的剩余开销是类描述符数据的写入;类名称,它具有的字段以及它们是什么类型等等。如果java对象是xml,它就像首先编写xsd,以便知道结构,然后写入没有标签的xml数据。在某些情况下,它实际上是非常高效的,例如,如果您需要将100多个相同类类型的实例写入同一个流中 - 您将永远不会感受到一次写入类描述符数据的冲击流的开始。

但是,如果你只需要写一个该类的实例,也许没有其他的东西,那么有一种方法可以将事物转化为你的优势。不要将对象传递给流,这会导致类描述符首先被写入,然后是实际数据,将流传递给对象并直接转到数据写入部分。底线是你负责代码中的结构部分,而不是让ObjectOutput/ObjectInput执行它。

请注意,我也将您的班级从Map更名为TileMap。正如BalusC指出的那样,这不是一个好名字。

import java.io.*; 

public class TileMap implements Externalizable { 

    private String name; 
    private int[][] tiles; 

    public TileMap(String name, int[][] tiles) { 
     this.name = name; 
     this.tiles = tiles; 
    } 

    // no-arg constructor required for Externalization 
    public TileMap() { 
    } 

    public void writeExternal(ObjectOutput out) throws IOException { 
     out.writeUTF(name); 
     out.writeInt(tiles.length); 
     for (int x = 0; x < tiles.length; x++) { 
      out.writeInt(tiles[x].length); 
      for (int y = 0; y < tiles[x].length; y++) { 
       out.writeInt(tiles[x][y]); 
      } 
     } 
    } 

    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { 
     this.name = in.readUTF(); 
     this.tiles = new int[in.readInt()][]; 
     for (int x = 0; x < tiles.length; x++) { 
      tiles[x] = new int[in.readInt()]; 
      for (int y = 0; y < tiles[x].length; y++) { 
       tiles[x][y] = in.readInt(); 
      } 
     } 
    } 

} 

写是这样的:

public static void write(TileMap tileMap, OutputStream out) throws IOException { 
    // creating an ObjectOutputStream costs exactly 4 bytes of overhead... nothing really 
    final ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(out)); 

    // Instead of oos.writeObject(titleMap1) we do this... 
    tileMap.writeExternal(oos); 

    oos.close(); 
} 

和读是这样的:

public static TileMap read(InputStream in) throws IOException, ClassNotFoundException { 
    final ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(in)); 

    // instantiate TileMap yourself 
    TileMap tileMap = new TileMap(); 

    // again, directly call the readExternal method 
    tileMap.readExternal(ois); 

    return tileMap; 
}