【问题标题】:Writing HashMap contents to the file将 HashMap 内容写入文件
【发布时间】:2016-09-28 08:56:51
【问题描述】:

我有一个HashMap<Integer, Integer>。我将它的内容写入文件,所以它的每一行都包含hashmapKey:::hashmapValue。这就是我现在的做法:

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

我非常怀疑是否需要将整个 HashMap 复制到字符串列表中,我确信在处理大量数据时它会给我带来性能问题。我的问题是:如何使用 Java 8 将 HashMap 内容写入文件以避免在另一个列表中复制值?

【问题讨论】:

  • 您将编写代码的代码放在您的 lambda 中或使用 Map.entrySet 遍历地图
  • 你并不需要一个 DATA_SEPARATOR 因为 int 有一个固定的大小

标签: java hashmap java-8 nio java-stream


【解决方案1】:

最简单、非复制、最“流式”的解决方案是

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

虽然 Stream 未实现 Iterable,但可以执行以在流上调用 iterator() 结束的 Stream 操作的 lambda 表达式。它将履行契约,因为 lambda 表达式与 Stream 不同,在每次调用时都会生成一个新的 Iterator

请注意,我删除了显式 UTF-8 字符集说明符,因为 java.nio.Files 在未指定字符集时将使用 UTF-8(与旧的 io 类不同)。

上述解决方案的巧妙之处在于 I/O 操作包装了 Stream 处理,因此在 Stream 内部,我们不必处理已检查的异常。相比之下,Writer+forEach 解决方案需要将IOExceptions 处理为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(); }

【讨论】:

  • 请注意(虽然这是合理的假设),但并未指定 Files.write 仅遍历 Iterable 一次。
  • @Tagir Valeev:如果 JRE 在多次执行此代码时重用 lambda 实例(假设它捕获相同的 Map 实例),这也在规范范围内。这是确保在每次调用时正确生成新迭代器的另一个原因。
【解决方案2】:

您可以简单地避免使用List&lt;String&gt;,方法是将行直接写入磁盘,例如使用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();

【讨论】:

  • 你应该使用try-with-resource
  • 除此之外,此解决方案不起作用,因为必须捕获 Writer.write 可能抛出的 IOExceptions(这将使 lambda 表达式显着复杂化)...
  • 我故意省略了 try-catch 或 finally 环境,因为它只是一个代码 sn-p 显示如何做到这一点,而不是一个功能齐全的程序。
  • 如果你知道它,你应该知道它允许省略显式的 close() 调用(flush() 无论如何已经过时了),所以再次,它会让您的代码更简单,因此没有理由不在答案中使用它。您的代码过于复杂,无法确保安全关闭。除此之外,它等于在finally块中关闭,存在根本的语义差异。
  • @Jagesh Maharjan 这与流 API 无关。 FileOutputStream 有一个构造函数,它接受 boolean 作为第二个参数。如果将其设置为true,它会将数据附加到现有文件中。
【解决方案3】:

您可以将映射的条目映射到字符串并将它们写入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);
    }
}

【讨论】:

    【解决方案4】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-11-15
      • 2012-05-18
      • 1970-01-01
      • 1970-01-01
      • 2013-08-01
      • 2010-11-09
      • 2018-01-08
      • 1970-01-01
      相关资源
      最近更新 更多