如何在Java 8中使用Streams将HashMap转换为K / V字符串

7

我希望尽快创建一个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函数中追加键和值看起来很耗费资源,不是吗?

3
在对这两种方法进行剖析后,哪一种运行速度比另一种更快?快了多少? - Sean Bright
4
除非性能是系统的关键部分,并且使用分析工具(如性能分析器)指出它是瓶颈,否则在这种情况下我不会担心性能。如果您之前没有使用过这种工具,但认为此代码不够优化,那么可能您是错的,应该先进行测试。 - Luiggi Mendoza
顺便提一句,对于您来说,m.toString()的结果可能足够接近,这并不重要。 - Sean Bright
你尝试过使用 parallelStream 吗? - Anonymous Coward
@AnonymousCoward 并行流被夸大其词了,如果不知道实际应用场景(尤其是在Web服务器中)就建议使用时要小心。 - Adam Dyga
1
请注意,在JDK 9中,StringJoiner(这是Collectors.joining的基础类)进行了优化,因此流实现的速度比JDK 8快约20-25%(尽管仍然不如朴素方法快)。 - Tagir Valeev
3个回答

8
map -> map.entrySet().stream().map(Entry::toString).collect(joining(";", "[", "]"))

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

Luiggi Mendoza所说:

在这种情况下,除非它是系统的关键部分,并通过使用分析器或类似工具指出为瓶颈,否则不必担心性能。如果您以前没有做过这件事,并且认为此代码不是最佳选择,则我知道可能您是错的,应该先测试它。


3
OP 的 "Collectors.joining()" 调用中分隔符、前缀、后缀的顺序不正确,您的解决方案已经修复了这个问题。 - Richard Lewan
3
请注意,虽然Entry.toString()通常返回key = value,但这不是Entry接口明确要求的,因此您可能会遇到返回其他内容的映射实现。我更喜欢像问题中那样显式地连接字符串。 - Tagir Valeev
我已经查看了Entry接口。我没有找到任何声明toString的地方。你能解释一下Entry::toString()是如何定义的吗?另外,只有Entry::toString在我的端上无法解决。我需要执行Map.Entry::toString() - seal
1
@seal toString() 是由 java.lang.Object 声明的,因此它被所有东西继承。如果您执行 import java.util.Map.Entry;,那么您可以直接使用 Entry::toString - gdejohn
@gdejohn 很难理解接口如何找到 toString 方法的,既没有超级接口,也没有继承自 Object 类的接口,那么接口怎么能使用 toString 方法呢? - seal
1
@seal http://docs.oracle.com/javase/specs/jls/se8/html/jls-9.html#jls-9.2 - gdejohn

0

使用StringBuilder代替Buffer。

Javadoc => Class StringBuffer

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


这个解决方案比使用流更快吗?两者可以结合使用吗? - Joel
虽然在第一段代码中使用StringBuilder可能比StringBuffer更快,但问题特别涉及第二段代码的速度,正如@LuiggiMendoza所指出的那样。 - FThompson
1
对于读者而言,在第二种情况下编译器会为这段代码创建一个 StringBuilderentry.getKey() + " = " + entry.getValue()),因此在每次迭代中无需使用另一个 StringBuilder。而且,Collectors#joining 将使用一个 StringJoiner,该 StringJoiner 在幕后使用了一个 StringBuilder,我想使用 StringBuilder 没有真正的改进。 - Luiggi Mendoza
1
@LuiggiMendoza,顺便说一下,在JDK 9中不再使用StringBuilder了。 - Tagir Valeev

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

1
将每对打印到控制台单独需要很长时间。将所有内容收集到StringBuilder中,然后打印可能更快。 - Anonymous Coward
如果你想进行微基准测试,你也应该考虑字符串构建器中字符数组扩展的情况(默认情况下会创建char [16],这也是代价高昂的) :) - rgrebski
1
如果您想直接打印地图,则不需要使用“Stream”。只需使用map.forEach((k,v) -> System.out.println(k+":"+v)); - Holger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接