【问题标题】:How to convert a HashMap to a K/V-String in Java 8 with Streams如何在 Java 8 中使用 Streams 将 HashMap 转换为 K/V-String
【发布时间】:2015-09-26 23:45:00
【问题描述】:

我想尽快创建我的HashMap<String, String> m 的键值对字符串。

我试过了:

StringBuffer buf = new StringBuffer();
buf.append("[");
for (String key : m.keySet()) {
   buf.append(key);
   buf.append("=");
   buf.append(m.get(key));
   buf.append(";");
}
buf.append("]");

我尝试过使用 Java8:

m.entrySet().stream()
            .map(entry -> entry.getKey() + " = " + entry.getValue())
            .collect(Collectors.joining("; " , "[" , "]"));

有没有更快、更好的代码来做到这一点? 在 map 函数中附加键和值似乎很昂贵,不是吗?

【问题讨论】:

  • 当您分析这两种方法时,哪一种运行速度比另一种快?快多少?
  • 我不会担心这种情况下的性能除非它是系统的关键部分,并且通过使用分析器或类似工具被指出为瓶颈。如果您之前没有这样做过并且您认为此代码不是最佳的,那么 I̶ ̶k̶n̶o̶w̶̶t̶h̶a̶t̶ 也许您错了,应该先测试一下。
  • FWIW - m.toString() 的结果可能对你来说已经足够接近了,这无关紧要。
  • 你试过使用parallelStream吗?
  • 请注意,在 JDK 9 StringJoiner(这是 Collectors.joining 的底层类)中进行了优化,因此流实现将比在 JDK 8 中快 20-25%(尽管仍然没有天真烂漫)。

标签: java hashmap java-8 java-stream


【解决方案1】:

使用 StringBuilder 代替缓冲区。

Javadoc => 类 StringBuffer

“通常应优先使用 StringBuilder 类,因为它支持所有相同的操作,但速度更快,因为它不执行同步。” Class StringBuffer

【讨论】:

  • 它是否比使用流的解决方案更快?两者可以结合吗?
  • 虽然在第一段代码中使用 StringBuilder 可能比 StringBuffer 更快,但正如@LuiggiMendoza 所指出的那样,问题具体在于第二段代码的速度。
  • 对于读者来说,编译器在第二种情况下为这段代码创建一个StringBuilderentry.getKey() + " = " + entry.getValue()) 在每次迭代中,所以没有必要使用另一个StringBuilder。而Collectors#joining 将使用StringJoiner 在幕后为您使用StringBuilder,我想使用StringBuilder 根本没有真正的改进。
  • @LuiggiMendoza,顺便说一句,在 JDK 9 中它不再使用 StringBuilder
【解决方案2】:
@Test
public void testMapPrint() {
    Map<String, String> map = new HashMap<>();
    map.put("a", "b");
    map.put("c", "d");

    map.entrySet().stream()
            .forEach(entry -> System.out.println(entry.getKey() + ":" + entry.getValue()));
}

【讨论】:

  • 将每一对分别打印到控制台需要很长时间。在 String{Builder,Builder} 中收集所有内容然后打印它很可能更快。
  • 好吧,如果你想进行微基准测试,你还应该考虑字符串构建器中的 char 数组正在扩展的情况(它也很昂贵,默认情况下会创建 char[16]):)
  • 如果您想直接打印地图,则不需要Stream。只需使用map.forEach((k,v) -&gt; System.out.println(k+":"+v));
【解决方案3】:
map -> map.entrySet().stream().map(Entry::toString).collect(joining(";", "[", "]"))

(请注意,我省略了导入。)

就像Luiggi Mendoza说的:

我不会担心这种情况下的性能,除非它是系统的关键部分,并且通过使用分析器或类似工具被指出为瓶颈。如果您以前没有这样做过并且您认为此代码不是最佳的,那么 I̶ ̶k̶n̶o̶w̶ ̶t̶h̶a̶t̶ 也许您错了,应该先测试一下。

【讨论】:

  • OP 的 'Collectors.joining()' 调用的分隔符、前缀、后缀顺序错误。你的解决方案解决了这个问题。
  • 请注意,虽然Entry.toString() 通常返回key = value,但Entry 接口并未明确要求它,因此您可能会点击返回其他内容的地图实现。我更喜欢问题中的明确连接。
  • 我看过Entry界面。我无法对任何声明 toString 进行罚款。你能解释一下Entry::toString()是如何定义的吗?另一件事,只有Entry::toString 无法解决我的问题。我需要做Map.Entry::toString()
  • @seal toString() 是由java.lang.Object 声明的,所以它被所有东西继承。如果你使用import java.util.Map.Entry;,那么你可以直接使用Entry::toString
猜你喜欢
  • 2013-04-13
  • 2014-05-09
  • 2019-01-23
  • 2021-06-22
  • 1970-01-01
  • 2014-09-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多