Как эффективно записать большую структуру данных в файл?

У меня есть переменная типа HashMap<String, HashSet<Long>> и его размер может вырасти до 100 МБ. Мне нужно записать это на вторичное хранилище.

Сериализация не вариант, так как он слишком медленный для меня. Есть ли какой-либо другой лучший способ выгрузить байтовую структуру на жесткий диск?

PS: меня беспокоит только скорость записи на диск, медленное чтение не проблема.

2 ответа

Решение

Вы можете сериализовать это самостоятельно. Вы также можете сжать данные, чтобы сделать их меньше.

public static void write(String filename, Map<String, Set<Long>> data) throws IOException {
    try (DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(
            new DeflaterOutputStream(new FileOutputStream(filename))))) {
        dos.writeInt(data.size());
        for (Map.Entry<String, Set<Long>> entry : data.entrySet()) {
            dos.writeUTF(entry.getKey());
            Set<Long> value = entry.getValue();
            dos.writeInt(value.size());
            for (Long l : value) {
                dos.writeLong(l);
            }
        }
    }
}

Чтобы прочитать это, вы просто делаете то же самое, но читаете вместо того, чтобы писать.

public static Map<String, Set<Long>> read(String filename) throws IOException {
    Map<String, Set<Long>> ret = new LinkedHashMap<>();
    try (DataInputStream dis = new DataInputStream(new BufferedInputStream(
            new InflaterInputStream(new FileInputStream(filename))))) {
        for (int i = 0, size = dis.readInt(); i < size; i++) {
            String key = dis.readUTF();
            Set<Long> values = new LinkedHashSet<>();
            ret.put(key, values);
            for (int j = 0, size2 = dis.readInt(); j < size2; j++)
                values.add(dis.readLong());
        }
    }
    return ret;
}

public static void main(String... ignored) throws IOException {
    Map<String, Set<Long>> map = new LinkedHashMap<>();
    for (int i = 0; i < 20000; i++) {
        Set<Long> set = new LinkedHashSet<>();
        set.add(System.currentTimeMillis());
        map.put("key-" + i, set);
    }
    for (int i = 0; i < 5; i++) {
        long start = System.nanoTime();
        File file = File.createTempFile("delete", "me");
        write(file.getAbsolutePath(), map);
        Map<String, Set<Long>> map2 = read(file.getAbsolutePath());
        if (!map2.equals(map)) {
            throw new AssertionError();
        }
        long time = System.nanoTime() - start;
        System.out.printf("With %,d  keys, the file used %.1f KB, took %.1f to write/read ms%n", map.size(), file.length() / 1024.0, time / 1e6);
        file.delete();
    }
}

печать

With 20,000  keys, the file used 44.1 KB, took 155.2 to write/read ms
With 20,000  keys, the file used 44.1 KB, took 84.9 to write/read ms
With 20,000  keys, the file used 44.1 KB, took 51.6 to write/read ms
With 20,000  keys, the file used 44.1 KB, took 21.4 to write/read ms
With 20,000  keys, the file used 44.1 KB, took 21.6 to write/read ms

Таким образом, 20 000 записей за 21 мс и использование только 2,2 байта на запись.

Используйте любую подходящую библиотеку сериализации (некоторые из них являются быстрыми - например, буферы протокола Google быстро работают и создают небольшие сообщения), чтобы получить данные в подходящей форме, затем сжать их в памяти и записать результаты на диск.

Время дискового ввода-вывода в большинстве случаев будет вашим главным узким местом, так что сжатие, чтобы уменьшить это, очень поможет.

Мы можем сделать это с помощью API Джексона.

Предварительные требования: добавьте следующие файлы Jar в путь к классам. Вы можете скачать их здесь.

  • Джексон Core
  • Аннотации Джексона
  • Джексон Датабинд

Здесь я собираюсь сделать пример для структуры данных HashMap>

Шаг 1. Создайте образец класса (DataStructure), который хранит вашу структуру данных как переменную.

public class DataStructure {
  public HashMap<String, HashSet<Long>> data = new HashMap<String, HashSet<Long>>();
  public DataStructure() {
  }
  public DataStructure(HashMap<String, HashSet<Long>> data) {
 this.data = data;
  }
}

Шаг 2: Создайте метод для хранения структуры данных в файле.

static void storeToFile(HashMap<String, HashSet<Long>> data) {
  try {
   String fileName = "test.txt";
   FileWriter fw = new FileWriter(fileName);
   DataStructure ds = new DataStructure(data);
   ObjectMapper objectMapper = new ObjectMapper();
   fw.write(objectMapper.writeValueAsString(ds));
   fw.close();
  } catch (IOException e) {
   System.out.println("storeToFile: " + e.getMessage());
  }
 }

После шага 2 ваша структура данных сохраняется в виде строки в указанном файле.

Для получения дополнительной информации: http://tutorials.jenkov.com/java-json/index.html

Я также написал сообщение в блоге о поиске: https://tech-scribbler.blogspot.com/2020/04/how-can-you-store-any-complex-data.html

Другие вопросы по тегам