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();
}
这是一样快,我能得到什么?
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();
'Map'应该实现'Serializable'。并给出一个更新版本的文档的链接。 – Bozho 2010-09-11 19:47:54
类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执行的两件事:您可以使用通道和缓冲区来简单读写。我很怀疑只读一个文件中的数据会带来性能上的好处。或者,您可以存储映射文件,并让内核分页输入和输出数据。 可能适合您,取决于数据总量和涉及的内存配置。
读入文件将是时间关键的。每个文件将成为整个地图的一部分。所以每张地图的尺寸可能只有30x30到50x50,但随着角色的移动,我将不得不在飞行中阅读它们。 – 2010-09-11 19:53:00
@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;
}
注意到应该是存在一个很普遍的使用'java.util中。地图'类。您希望将您的'Map'类重命名为不同的名称,以避免名称冲突和其他开发人员混淆(重新)查看您的代码。 – BalusC 2010-09-11 20:13:29