Java Binary IO Написание и чтение - PullRequest
2 голосов
/ 11 сентября 2010

У меня есть такая структура, которую я хочу читать и записывать в файл, и я хочу сделать это как можно быстрее.

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

Каков наилучший способ сделать это? Я в основном программист на C ++, и я не знаю лучшего способа сделать это на Java. Кажется, что это должно быть действительно просто, но я не знаю, как сделать двоичный ввод в Java.

Это то, что я создал до сих пор:

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();
}

Это так быстро, как я могу получить?

Ответы [ 4 ]

3 голосов
/ 11 сентября 2010

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();
3 голосов
/ 11 сентября 2010

Если все, что вы хотите сделать, это написать эту единственную структуру, то вам следует вручную сериализовать и десериализовать код. Я бы порекомендовал написать счетчик, затем строковые символы, затем размеры массива, а затем все целые числа. Вам нужно будет побеспокоиться о порядке байтов для себя, взяв два байта для каждого символа и четыре для каждого целого.

Не используйте сериализацию Java, если вам нужна скорость, и не используйте NIO только для однопоточной дисковой операции ввода-вывода файла. Используйте буферизованный поток.

Вы уверены, что это действительно критичная для производительности операция? Во всяком случае, насколько велик этот массив?

Рассматривали ли вы возможность отображения памяти в NIO? Теперь вы заставляете ядро ​​выполнять тяжелую работу. Создание большого количества крошечных файлов, вероятно, в любом случае вызовет у вас изжогу. Обратите внимание, что я различаю две вещи, которые вы можете делать с NIO: вы можете использовать каналы и буферы для простого чтения и записи. Я весьма сомневаюсь в том, что преимущество в производительности заключается в том, чтобы просто читать данные из файла. Или вы можете использовать файлы карт памяти и позволить ядру загружать и выводить данные. Это может работать хорошо для вас, в зависимости от общего объема данных и конфигурации используемой памяти.

1 голос
/ 12 сентября 2010

У меня есть очень специфическая техника, которую я использую для такого рода вещей. Это своего рода гибридный подход, который, как я считаю, приводит к самому высокопроизводительному базовому коду io, но при этом поддерживает читабельность и совместимость с простой сериализацией Java.

Отражение, используемое в сериализации Java, исторически считалось медленным и медленным. Но после добавления sun.misc.Unsafe эта часть на самом деле невероятно быстра. Первоначальное обращение к первому вызову clazz.getDeclaredFields () и других методов типа «getDeclared» из java.lang.Class все еще сохраняется, но они кэшируются на уровне виртуальной машины, поэтому после первого стоят очень дешево (очень заметно) удар.

Оставшиеся издержки 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;
}
0 голосов
/ 11 сентября 2010

Если вы ищете самый быстрый способ ввода / вывода в Java, взгляните на классы nio (небуферизованный io). Я вполне уверен, что вы также можете выполнять бинарный ввод / вывод.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...