Java - 读取和存储数据最快、使用最少的方法

3
我使用Java(LWJGL)创建了一款游戏,想知道在硬盘上读取和存储数据的最快方法。游戏的世界是一种代表3D世界的网格,我需要存储每个1x1x1大小的立方体(或者在未来可能会更低以获得更好的分辨率)的数据。我目前正在将每个立方体记录保存在文本文件中。每个记录存储32x32x32的立方体数据。
现在,每个记录在硬盘内存中占用625KB,并且读取文件需要16毫秒(在我的电脑上,仅读取它,而不做其他事情)。
我想知道是否有更好的方法,因为我可能每3秒钟就要使用这种方法(大多数情况下不需要,但有时候需要 - 需要保持帧速率为60)。一种方法可以减少内存消耗并加快读取速度。文本文件仅使用数字,英文字母和一些特殊字符(','(')' '{' '}' '-' '.' ':' ,也许还有更多)。
我听说Minecraft使用.mca文件,但我不知道它们是否更好,或如何读取它们。

它是如何存储的?你能举个例子吗? - Maarten
2个回答

0
一种实现你想要的方法是使用序列化和对象流将二进制数据存储到文件中。基本上,你将会存储你的数组对象,或者地形块的对象(或者你称之为地形子区块的任何东西)。这样可以实现更快的加载时间,因为JVM在加载时不需要重新构建每个对象。
使用二进制文件的主要优点是,当你希望存储数据时,你不必编写任何算法,甚至不必迭代数据。由于你提到你想频繁存储,这可能是一个很好的解决方案。
以下是一个示例,以便给你一些指导。
假设:
class Chunk implements java.io.Serializable {

    private static final long serialVersionUID = 1L;

    Block[][][] chunkBlocks;
    // ... other code //
}

通过在类定义中添加语句implements Serializable,您表示该类可用于从字节流中读取和写入对象,从而允许您将它们存储并从文件中读取。还有一件事要注意的是变量serialVersionUID。这非常重要,因为它将告诉流这个对象属于哪个类,以及是否可以将其强制转换为该类。(注意:这是一个粗略的近似,但出于不冗长的考虑,我会跳过细节)。现代IDE(如eclipseIntelliJ)将提示您在实现Serializable接口时向类中添加此变量。它们还会为您提供生成唯一ID的选项。建议这样做以确保没有冲突。
最后,以下是如何存储对象的方法:
/**
 * Object o         : the object you want to save
 * String filename  : the file path of where you want to save the object
 */
public static void save(Object o, String filename) {

    FileOutputStream fos = null;
    ObjectOutputStream oop = null;

    try {
        fos = new FileOutputStream(filename);
        oop = new ObjectOutputStream(fos);
        oop.writeObject(o);
        oop.flush();
        fos.flush();
        oop.close();
        fos.close();
    } catch (IOException e) {
        e.printStackTrace();
    }

}

并加载一个:

/**
 * @returns Object o : the object stored in the file @ filename
 * String filename   : the file path of where you want to save the object
 */
public static Object load(String filename) {

    FileInputStream in;
    ObjectInputStream ois;

    Object o = null;

    try {
        in = new FileInputStream(filename);
        ois = new ObjectInputStream(in);
        o = ois.readObject();
        ois.close();
        in.close();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
   }

   return o;

}

在上面的代码中,我将函数概括为接受和返回java.lang.Object,但如果您希望有一个专门针对特定类的函数,您可以更改它为Chunk或其他内容。
您还提到了Minecraft文件格式,但这些格式非常应用程序特定,并且基本上是以它自己能理解的格式。除此之外,它们不适合频繁保存,而是更适合后台块卸载。
希望这有所帮助!

我不知道你从哪里得到了“JVM在加载时不必重新构造每个对象”的想法。这正是反序列化所做的。FileInputStreamFileOutputStream的显式close()是多余的。 - user207421
有人可能会争辩说反序列化并不等同于构造,因为所有属性都(希望如此)填充了预定值 - 对象实例本身是新的,但仅此而已——它们的内容并不是(只限于原始属性)。但我并不认同这种观点……我只是说一下。 - specializt
你说得对,我想表达的是他必须根据从文件中读取的内容显式地构造每个对象,这是一个繁琐的过程。 - claudehenry

0

访问您提到的固定大小文件的绝对最快方法是通过NIO MappedByeBuffer。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接