【问题标题】:Use string replace inside java 8 foreach在 java 8 foreach 中使用字符串替换
【发布时间】:2020-01-08 12:07:24
【问题描述】:

我已经看过this questions,但我的问题有点不同。

我有一个“baseString”、n HashMap 和一个输出字符串。 我想用hashmap填充baseString,构造一个json调用参数。

我已经用 Java 7 做到了,用这种方式:

HashMap<String,Integer> element= getAllElement();

String baseElem="{\"index\":{}}\r\n" + 
        "{\"name\":\"$name$\",\"age\":$age$}";

String result=baseElem;

for (Map.Entry<String, Integer> entry : element.entrySet()) {

    result=result.replace("$name$", entry.getKey());
    result=result.replace("$age$", entry.getValue().toString());
    result=result+baseElem;
}
result= result.replace(baseElem, "");

现在我想对 Java 8 也一样, 我试过这样:

element.forEach((k,v)->{
            result=result.replaceAll("$name$", k);
            result=result.replaceAll("$age$", v.toString());
            result=result+baseElem;
        });

但是对于每个结果我都有一个错误

"在封闭范围内定义的局部变量结果必须是最终的或 有效地最终”

所以问题是:我可以用 Java 8 和流以某种方式做到这一点吗?或者没有办法,所以我可以使用简单的Java 7 for

【问题讨论】:

  • 将变量定义为类变量,而不是在方法内部定义。
  • 这不是在 Java 中做 JSON 的正确方法,您应该使用 Jackson 将 Java 对象转换为 JSON 并将 JSON 转换为 Java 对象。如果你坚持使用模板,还有freemarker、mustache、thymeleaf、...等更好的解决方案。
  • 请注意,replaceAll 需要一个正则表达式,而$ 是一个表示输入结束边界的特殊字符。它只能匹配输入字符串的结尾,所以$name$$age$ 永远不会匹配。
  • @NyamiouTheGaleanthrope 感谢您的建议,我正在尝试所有可能的解决方案和实施。

标签: java for-loop java-8 java-stream


【解决方案1】:

您的方法完全走错了方向。这不仅与 Stream API 采用的函数式编程风格相矛盾。即使是循环也非常低效,对不断增长的结果字符串执行重复的字符串连接和替换操作。

你做了一个Shlemiel the painter’s algorithm

您实际上只想对每个映射条目的baseElem 模板执行replace 操作并连接所有结果。这可以直接表示:

Map<String,Integer> element = getAllElement();

String baseElem = "{\"index\":{}}\r\n{\"name\":\"$name$\",\"age\":$age$}";

String result = element.entrySet().stream()
    .map(e -> baseElem.replace("$name$", e.getKey())
                      .replace("$age$",  e.getValue().toString()))
    .collect(Collectors.joining());

【讨论】:

  • 感谢您的所有建议,我对 Java 8 还很陌生,所以我正在努力学习并理解我的错误。
【解决方案2】:

至于“在封闭范围内定义的局部变量结果必须是最终或有效最终”请参阅this答案以获得进一步解释。

至于:

所以问题是:我可以用 Java 8 以某种方式做到这一点,并且 流?还是没有办法,所以我可以用简单的Java 7 来做?

您使用迭代方法执行的逻辑在函数世界(即流)中称为“折叠”或“减少”。

那么,你要做的是:

String result = element.entrySet()
                .stream()
                .reduce(baseElem,
                        (e, a) -> e.replace("$name$", a.getKey()).replace("$age$",
                                a.getValue().toString()),
                        (x, y) -> {
                            throw new RuntimeException();
                        });

reduce 的第三个输入参数称为组合函数,它应该是一个用于组合两个值的关联、非干扰、无状态函数,它必须与累加函数兼容

如果您不打算使用并行流,那么当前逻辑就足够了,否则您需要将 (x, y) -&gt; {...} 替换为实际逻辑。

【讨论】:

  • 没有必要以这种方式滥用reduce,一旦你意识到循环有什么问题。 clean solution 简单高效。
  • 与加入的不同,此解决方案不会连接事物,因此输出字符串正是输入字符串及其替换。所以这一切都取决于需要......而这个完全符合我今天的需求:p Thx
【解决方案3】:

它认为,你可以使用这个:

private static Map<String, Integer> getAllElement() {
    Map<String, Integer> map = new HashMap<>();
    map.put("\\$name\\$", 666);
    map.put("\\$age\\$", 777);
    return map;
}

Map<String, Integer> map = getAllElement();
String[] json = { "{\"index\":{}}\r\n{\"name\":\"$name$\",\"age\":$age$}" };

map.forEach((key, val) -> json[0] = json[0].replaceAll(key, String.valueOf(val)));
System.out.println(json[0]);

输出:

{"index":{}}
{"name":"666","age":777}

【讨论】:

    【解决方案4】:

    来自文档,

    使用的任何局部变量、形式参数或异常参数,但 未在 lambda 表达式中声明的必须声明为 final 或 有效地是最终的(§4.12.4),或者发生编译时错误 尝试使用。 变量使用的类似规则适用于内部类的主体(第 8.1.3 节)。对有效最终变量的限制禁止访问动态变化的局部变量,这些变量的捕获可能会引入并发问题。相比最终的限制,减少了程序员的文书负担。

    这里的常量是reference,而不是值。

    您收到此异常是因为您正在更改 result 的引用。您正在将 result 重新分配给 lambda 中的某个其他字符串。因此,与 JLS 冲突,动态变化的局部变量

    此外,您还可以使用 Jackson ObjectMapper 从 Java 对象生成 JSON,而不是硬编码和替换内容。

    【讨论】:

      【解决方案5】:

      在您的方法之外定义结果变量,如下所示

      Class A{
      String result=null;
      Method a(){
      //method implementation 
      }
      }
      

      【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-09-08
      • 1970-01-01
      • 2018-03-02
      • 2015-05-01
      • 2016-07-17
      • 2013-07-09
      • 1970-01-01
      相关资源
      最近更新 更多