2014-01-16 4 views
1

У меня есть переменная типа HashMap<String, HashSet<Long>> и ее размер может вырасти до 100 МБ. Мне нужно записать это на вторичное хранилище.Как эффективно написать большую структуру данных в файл?

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

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

ответ

1

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

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 

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

+0

Это сработало, но я действительно не понял поток. Как мне это прочитать? Обычный десериализатор не будет работать. – kBisla

0

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

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

+0

Мой профессор не разрешает google :( – kBisla

Смежные вопросы