我有这种结构,我想要读取和写入文件,我想以最快的方式做到这一点。
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 :(得分:3)
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 :(得分:3)
如果您只想编写一个结构,那么您应该手动编写序列化和反序列化。我建议写一个计数,然后是字符串字符,然后是数组的维度,然后是所有整数。您将不得不为自己担心字节顺序,每个字符串需要两个字节,每个字符串需要四个字节。
如果您需要速度,请不要使用Java序列化,并且不要仅针对单线程磁盘文件I / O情况使用NIO。请使用缓冲流。
您确定这确实是一项对性能至关重要的操作吗?无论如何,这个数组有多大?
您是否考虑过NIO的内存映射功能?现在你正在使内核完成繁重的任务。制作许多小文件可能会让你无论如何都会感到胃灼热。请注意,我区分了您可以使用NIO做的两件事:您可以使用通道和缓冲区来进行简单的读写操作。我非常怀疑它只是从文件中读取一大堆数据的性能优势。或者,您可以存储映射文件,并让内核页面输入和输出数据。 可能适合您,具体取决于数据的总量和所涉及的内存配置。
答案 2 :(得分:1)
我有一种非常具体的技术用于此类事情。这是一种混合方法,我在最高性能的基本io代码中找到了结果,同时保持了可读性和与普通Java序列化的兼容性。
Java序列化中使用的反射是历史上认为缓慢的部分,而且速度很慢。但是自从添加sun.misc.Unsafe
以来,该部分实际上非常快。第一次调用clazz.getDeclaredFields()和java.lang.Class的其他'getDeclared'类型方法仍然是最初的命中,但这些方法在VM级别缓存,所以在第一次之后是低成本(非常明显)击中。
Java Serialization的剩余开销是编写类描述符数据;类名,它有哪些字段以及它们是什么类型等。如果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;
}
答案 3 :(得分:-1)
如果您正在寻找使用Java进行I / O的最快方法,请查看nio(无缓冲的io)类。我非常有信心你也可以做二进制I / O.
http://download.oracle.com/javase/1.4.2/docs/api/java/nio/package-summary.html