2010-09-11 203 views
2

我有這種結構,我想要讀取和寫入文件,並且我想盡可能以最快的方式來完成。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

回答

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

'Map'應該實現'Serializable'。並給出一個更新版本的文檔的鏈接。 – Bozho 2010-09-11 19:47:54

+0

類Map需要實現接口Serializable才能正常工作 – 2010-09-11 19:48:43

3

如果你想要做的是寫一個和唯一的結構,那麼你應該手工編寫系列化和反序列化。我建議寫一個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

1

我有一個非常具體的技術,我用這種東西。這是一種混合方法,我可以在最高性能的基本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; 
}