如何使用流将所有映射键值连接成一个字符串,并对每个值进行替换?

时间:2015-07-19 01:03:13

标签: java java-stream

我是一个Java 7人,并且被命令永远不再使用for-loop,只有部分舌头在脸颊。我需要一个Map<String, String>,最后得到一串命令行参数,如下面代码的输出(暂时忽略“streamy”行):

forLoop: --name5 "value5"--name4 "value4"--name3 "value3"--name2 "{\"x\": \"y\"}"--name1 "value1"
resetAllValues: --name5 "value5"--name4 "value4"--name3 "value3"--name2 "{\"x\": \"y\"}"--name1 "value1"
streamy: --name5 "value5"--name5 "value5"--name4 "value4"--name5 "value5"--name5 "value5"--name4 "value4"--name3 "value3"--name5 "value5"--name5 "value5"--name4 "value4"--name5 "value5"--name5 "value5"--name4 "value4"--name3 "value3"--name2 "{\"x\": \"y\"}"--name5 "value5"--name5 "value5"--name4 "value4"--name5 "value5"--name5 "value5"--name4 "value4"--name3 "value3"--name5 "value5"--name5 "value5"--name4 "value4"--name5 "value5"--name5 "value5"--name4 "value4"--name3 "value3"--name2 "{\"x\": \"y\"}"--name1 "value1"--name5 "value5"--name5 "value5"--name4 "value4"--name5 "value5"--name5 "value5"--name4 "value4"--name3 "value3"--name5 "value5"--name5 "value5"--name4 "value4"--name5 "value5"--name5 "value5"--name4 "value4"--name3 "value3"--name2 "{\"x\": \"y\"}"--name5 "value5"--name5 "value5"--name4 "value4"--name5 "value5"--name5 "value5"--name4 "value4"--name3 "value3"--name5 "value5"--name5 "value5"--name4 "value4"--name5 "value5"--name5 "value5"--name4 "value4"--name3 "value3"--name2 "{\"x\": \"y\"}"--name1 "value1"

名称/键保持不变。值必须放在双引号中,并且所有现有的双引号都需要转义(\")。关闭引号应该后跟一个空格。

我在forLoop中使用了pre-java-8方式,部分使用了resetAllValues中的流,这实际上改变了每个条目。 streamy一个是错误的,因为它反复将所有当前输出附加到下一个元素上...

如何通过流有效地完成这项工作?如何以不改变地图条目或使用构建器的方式完成?我还没有看到它。

package working;

import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

public class Temp {
   public static void main(String[] cmd_lineParams) {
      Map<String, String> map = new HashMap<>(5);
      map.put("name1", "value1");
      map.put("name2", "{\"x\": \"y\"}");
      map.put("name3", "value3");
      map.put("name4", "value4");
      map.put("name5", "value5");
      forLoop(map);
      resetAllValues(new HashMap<String, String>(map));
      streamy(new HashMap<String, String>(map));
   }
   private static final Matcher matcher = Pattern.compile("\"").matcher("ignored input");
   private static final void forLoop(Map<String, String> map) {
      StringBuilder builder = new StringBuilder();
      for(Map.Entry<String, String> entry : map.entrySet()) {
         String value = matcher.reset(entry.getValue()).replaceAll("\\\\\"");
         builder.append("--").append(entry.getKey()).append(" \"").append(value).append("\"");
      }
      System.out.println("forLoop: " + builder.toString());
   }

(续)

   private static final void resetAllValues(Map<String, String> map) {
      map = map.entrySet().stream()
         .collect(Collectors.toMap(entry -> entry.getKey(),
                                   entry -> matcher.reset(entry.getValue()).replaceAll("\\\\\\\"")));
      StringBuilder builder = new StringBuilder();
      for(Map.Entry<String, String> entry : map.entrySet()) {
         builder.append("--").append(entry.getKey()).append(" \"").append(entry.getValue()).append("\"");
      }
      System.out.println("resetAllValues: " + builder.toString());
   }
   private static final void streamy(Map<String, String> map) {
      StringBuilder builder = new StringBuilder();
      map.forEach((k,v) -> builder.append(
         builder.append("--").append(k).append(" \"").append(
            matcher.reset(v).replaceAll("\\\\\"")).append("\"")));
      System.out.println("streamy: " + builder.toString());
   }
}

(我九岁的孩子说我需要在这个问题的某个地方说“难度”。所以:难度。)

2 个答案:

答案 0 :(得分:12)

我想既然你说难度,我就得回答这个问题! :)

map.entrySet().stream().map((entry) -> //stream each entry, map it to string value
            "--" + entry.getKey() + " \"" + entry.getValue().replaceAll("\"", "\\\\\"") + "\"")
            .collect(Collectors.joining(" ")); //and 'collect' or put them together by joining

我个人不喜欢使用流,因为它很快变得丑陋,但它对于更简单的循环很有用。 (例如,使用某些字符加入所有值)但是,您可以使用parallelStream()代替stream()

轻松地对其进行并行化

如果你想要某种顺序的值,所以它不是那么随机(就像HashMap那样)你可以在map之前进行排序:

.stream().sort((e1, e2) -> e1.getValue().compareTo(e2.getValue()))
.map(...)...

请记住,这些是Map.Entry个对象。

更新:下面的Tagir Valeev的回答更好,因为它显示了最佳做法,而不仅仅是让它发挥作用。它也弥补了我几年前写的这个回答时遇到的溪流+ lambdas的初步抱怨(变得太难看了)。

答案 1 :(得分:6)

始终尝试将复杂问题分解为简单且独立的部分。这里(对于流和非流解决方案)最好将转义代码放入单独的方法中:

static String quoteValue(String value) {
    return "\"" + value.replaceAll("\"", "\\\\\"") + "\"";
}

通过这种方式,您可以使用具有清晰语义的单独代码,可以单独重复使用和测试。例如,将来您可能还需要转义反斜杠符号,因为目前您可能在解码最初混合了反斜杠和引号的字符串时遇到问题。如果你有一个单独的quoteValue方法,那么调试这个问题要容易得多(这样你就不应该创建测试图,只测试字符串)。

之后,流解决方案变得不那么混乱了:

map.entrySet().stream().map(entry ->
        "--" + entry.getKey() + " "+ quoteValue(entry.getValue()))
        .collect(joining(" "));

您可以进一步添加一个方法来格式化整个条目:

static String formatEntry(String key, String value) {
    return "--" + key + " " + quoteValue(value);
}

map.entrySet().stream().map(e -> formatEntry(e.getKey(), e.getValue()))
                       .collect(joining(" "));