Java安全地替换字符串而不泄漏

时间:2014-12-22 11:11:12

标签: java string replace

我需要专家建议以下方案。我有一个字符串模板(让我们假设电子邮件模板)如下。

Dear {PERSON_NAME},
We would like to thank you on behalf of {CEO_NAME}, {COMPANY_NAME}. You have been selected
for the position of {POSITION_NAME} in {DEPARTMENT_NAME}
etc etc

这是替换代码

String body = getTemplateBody(tempalteId);
sendMail( 
        body.replace("{PERSON_NAME}", personName )
            .replace("{CEO_NAME}", ceoName),
            .replace("{COMPANY_NAME}", companyName),
            .replace("{POSITION_NAME}", positionName),
            .replace("{DEPARTMENT_NAME}", deptName),
            .replace("{X}", someVar1),
            .replace("{Y}", someVar2),
            .replace("{Z}", someVar3),
            .replace("{ETC_ETC}", "etc")
        );

我们拥有什么:

  1. 使用{}附加的20个变量,例如{PERSON_NAME}
  2. 这些变量是固定的,不会在单个模板字符串中更改。
  3. 这些变量是唯一的,没有一个变量在同一模板中重复出现
  4. 每小时使用10,000个每个模板的实例。每天做(10,000 * 24)
  5. 问题:在模板字符串中替换变量以获取实际结果字符串的有效(非优雅)方法是什么 Efficient in terms of memory first and then processing

    以上代码中是否有memory leakageany problem

    请注意以上代码只是用简单的单词解释我的要求的示例,并且可能无法检查变量或方法名称编码标准。

4 个答案:

答案 0 :(得分:1)

这里FreeMarker不是一个好选择吗?如果您使用FreeMarker,您可以在单独的模板文件中提供模板,并通过将模板与数据模型(bean或Map)合并来构建输出。

// Setup config
Configuration cfg = new Configuration(Configuration.VERSION_2_3_21);
cfg.setDirectoryForTemplateLoading(new File("/where/you/store/templates"));
cfg.setDefaultEncoding("UTF-8");

// Create the data model - e.g. a bean or a map
final Map<String, String> root = new HashMap<>();
root.put("PERSON_NAME", "Joe");
root.put("CEO_NAME", "Anne");

// Get the template
Template temp = cfg.getTemplate("myTemplate.ftl");

// Merge the template with the data model
Writer out = new OutputStreamWriter(System.out);
temp.process(root, out);

答案 1 :(得分:0)

String::substring方法实现如下:

return Pattern.compile(target.toString(), Pattern.LITERAL)
    .matcher(this)
    .replaceAll(Matcher.quoteReplacement(replacement.toString()));

使用上面的代码,您可以创建9个Pattern18个中间String。由于每个调用都会内联替换值,因此您不会遇到内存泄漏,尽管最终String都会立即释放所有引用。

但是,您可以通过解析{ - }封闭值的输入并使用String组合最终StringBuilder来更有效地实现此功能。这应该是你更关心的问题。

答案 2 :(得分:0)

如果您使用HashMap作为替换持有者,则此解决方案具有线性运行时。 这个解决方案没什么特别的,只是简单的旧java;)

public static String format(CharSequence text, final Map<String, String> data)
{
    if (text == null)
    {
        return "";
    }

    final int len = text.length();

    if (len < 1)
    {
        return "";
    }

    final StringBuilder textSB = new StringBuilder();
    final StringBuilder keySB = new StringBuilder();
    int pos = 0;
    boolean inKey = false;
    char ch;
    String value;

    while (pos < len)
    {
        ch = text.charAt(pos);

        if (ch == '{')
        {
            if (inKey)
            {
                throw new IllegalArgumentException("Invalid open key char at " + pos + 1);
            }
            inKey = true;
            keySB.setLength(0);
        }
        else if (ch == '}')
        {
            if (!inKey)
            {
                throw new IllegalArgumentException("Invalid close key char at " + pos + 1);
            }
            inKey = false;
            value = data.get(keySB.toString());
            if (value == null)
            {
                throw new IllegalArgumentException("No value found for key " + keySB);
            }
            textSB.append(value);
        }
        else
        {
            if (inKey)
            {
                keySB.append(ch);
            }
            else
            {
                textSB.append(ch);
            }
        }
        pos++;
    }

    if (inKey)
    {
        throw new IllegalArgumentException("None determined key detected.");
    }

    return textSB.toString();
}

答案 3 :(得分:-1)

这个怎么样?

使用带有密钥的HashMap作为变量(即&#34; {PERSON_NAME}&#34;),并将该值作为此类密钥的相应获取者(即getPersonName())。显然,这个getter必须是Interface

恕我直言,这将是一个非常好的方式。

更有经验的人会怎么想?