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

ali*_*ind 5 java java-stream

我是一个Java 7人,并且被命令永远不再使用for循环,只有部分舌头在脸颊.我需要取一个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"
Run Code Online (Sandbox Code Playgroud)

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

我已经完成了pre-java-8方式forLoop,部分使用了流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());
   }
Run Code Online (Sandbox Code Playgroud)

继续...

   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());
   }
}
Run Code Online (Sandbox Code Playgroud)

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

Aar*_*jav 14

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

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
Run Code Online (Sandbox Code Playgroud)

我个人不喜欢使用流,因为它很快变得丑陋,但它对于更简单的循环很有用.(例如,使用某些字符连接所有值)但是,您可以通过使用parallelStream()而不是使用它来轻松地对其进行并行化stream()

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

.stream().sort((e1, e2) -> e1.getValue().compareTo(e2.getValue()))
.map(...)...
Run Code Online (Sandbox Code Playgroud)

请记住那些是Map.Entry物体.

更新:下面的Tagir Valeev的答案更好,因为它显示了最佳实践,而不仅仅是让它工作.它也弥补了我几年前写的这个答案(过于丑陋)时对流+ lambdas的初步抱怨.

  • `(e1,e2) - > e1.getValue().compareTo(e2.getValue())`可以替换为`Map.Entry.comparingByValue()`. (4认同)

Tag*_*eev 7

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

static String quoteValue(String value) {
    return "\"" + value.replaceAll("\"", "\\\\\"") + "\"";
}
Run Code Online (Sandbox Code Playgroud)

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

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

map.entrySet().stream().map(entry ->
        "--" + entry.getKey() + " "+ quoteValue(entry.getValue()))
        .collect(joining(" "));
Run Code Online (Sandbox Code Playgroud)

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

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

map.entrySet().stream().map(e -> formatEntry(e.getKey(), e.getValue()))
                       .collect(joining(" "));
Run Code Online (Sandbox Code Playgroud)