2016-05-30 5 views
2

У меня есть HashMap<Integer, Integer>. Я пишу его содержимое в файл, поэтому каждая его строка содержит hashmapKey:::hashmapValue. Это, как я делаю это сейчас:Запись содержимого HashMap в файл

List<String> mLines = new ArrayList<String>(); 
mHashMap.forEach((key, value) -> mLines.add(key + DATA_SEPARATOR + value)); 
Files.write(mOutputPath, mLines, StandardCharsets.UTF_8); 

Я очень сомневаюсь, что мне нужно скопировать весь HashMap в список строк, я уверен, что это даст мне проблемы производительности при работе с большими объемами данных. Мой вопрос: как я могу записать содержимое HashMap в файл с помощью Java 8, избегая копирования значений в другой список?

+2

Вы помещаете код, который делает запись внутри лямбда или перебирать карту с помощью 'Map.entrySet' – pvg

+0

вы на самом деле не нужен DATA_SEPARATOR поскольку int имеет фиксированный размер – Dexter

ответ

11

Самый простой, без копирования, наиболее решение «streamish» является

Files.write(mOutputPath,() -> mHashMap.entrySet().stream() 
    .<CharSequence>map(e -> e.getKey() + DATA_SEPARATOR + e.getValue()) 
    .iterator()); 

Хотя поток не реализует Iterable, лямбда-выражения выполнения операции поток, который заканчивается с заходом iterator() на поток , возможно. Он будет выполнять контракт, поскольку выражение лямбда будет, в отличие от Stream, создавать новый Iterator по каждому вызову.

Обратите внимание, что я удалил явный спецификатор набора символов UTF-8, так как java.nio.Files будет использовать UTF-8, если не задана кодировка (в отличие от старых классов io).

Упомянутое решение состоит в том, что операция ввода-вывода обертывает обработку потока, поэтому внутри потока нам не нужно иметь дело с проверенными исключениями. Напротив, решение Writer + forEach должно обрабатывать IOException с, так как BiConsumer не разрешено выбрасывать проверенные исключения. В результате, рабочий раствор с помощью forEach будет выглядеть так:

try(Writer writer = Files.newBufferedWriter(mOutputPath)) { 
    mHashMap.forEach((key, value) -> { 
     try { writer.write(key + DATA_SEPARATOR + value + System.lineSeparator()); } 
     catch (IOException ex) { throw new UncheckedIOException(ex); } 
    }); 
} catch(UncheckedIOException ex) { throw ex.getCause(); } 
+0

Обратите внимание, что (в то время как это разумное предположение) не указано, что 'Files.write' пересекает« Итерируемый »только один раз. –

+1

@ Тагир Валеев: Это также входит в спецификацию, если JRE повторно использует экземпляр лямбды (при условии, что он захватывает один и тот же экземпляр «Карта») при нескольких исполнениях этого кода. Это еще одна причина для правильного создания нового итератора при каждом вызове. – Holger

+0

Действительно, спасибо. –

-1

HashMap реализует Serializable, поэтому вы должны иметь возможность использовать стандартную сериализацию для записи hashmap в файл.

Пример:

HashMap<Integer, String> hmap = new HashMap<Integer, String>(); 

//Adding elements to HashMap 

try { 
    FileOutputStream fos = 
      new FileOutputStream("example.ser"); 
    ObjectOutputStream oos = new ObjectOutputStream(fos); 
    oos.writeObject(hmap); 
    oos.close(); 
    fos.close(); 
}catch(IOException ioe) { 
    ioe.printStackTrace(); 
} 
3

Вы можете просто избежать использования List<String> путем непосредственного выписывание линии на диск, используя, например, Writer:

Writer writer = new BufferedWriter(new OutputStreamWriter(
      new FileOutputStream(new File(mOutputPath)), StandardCharsets.UTF_8)); 
    mHashMap.forEach((key, value) -> writer.write(key + DATA_SEPARATOR + value + System.lineSeparator())); 
    writer.flush(); 
    writer.close(); 
+3

Вы должны использовать [try-with-resource] (https://docs.oracle.com/javase/8/docs/technotes/guides/language/try-with-resources.html). – Holger

+2

Кроме того, это решение не работает, поскольку 'IOException', потенциально брошенный 'Writer.write' должен быть уловлен (что значительно усложнит выражение лямбда) ... – Holger

+0

Я пропустил попытку или окончательную среду, преднамеренную, поскольку она просто фрагмент кода, показывающий, как сделать это не полноценной программой. – Robert

1

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

final Charset charset = Charset.forName("UTF-8"); 
try(FileChannel fc = FileChannel.open(mOutputPath, StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW)) { 
    mHashMap.entrySet().stream().map(e -> e.getKey() + ":::" + e.getValue() + "\n") 
      .map(s -> encode(charset, s)) 
      .forEach(bb -> write(fc, bb)); 
} 

void write(FileChannel fc, ByteBuffer bb){ 
    try { 
     fc.write(bb); 
    } catch (IOException e) { 
     throw new RuntimeException(e); 
    } 
} 

ByteBuffer encode(Charset charset, String string){ 
    try { 
     return charset.newEncoder().encode(CharBuffer.wrap(string)); 
    } catch (CharacterCodingException e) { 
     throw new RuntimeException(e); 
    } 
} 
Смежные вопросы