在我的应用程序中,我有一组参数和一组字符串模板,其中包含0..N参数(由花括号标识)。
例如,这是一组参数:
1 {o}
2 {o-country}
3 {d}
4 {d-country}
这是一个示例模板集(使用所有参数不是强制性的):
1 "flights from {o} to {d}"
2 "flights to {d-country}"
3 "flights from {o} to {d-country}"
应用程序允许用户为参数定义许多具体的值组合,然后将它们应用于模板集。在为参数提供具体值组合时,用户必须为每个现有参数提供值。
例如,这是一组具体的值组合:
1 <{o}="Milan",{o-country}="Italy",{d}="Madrid",{d-country}="Spain">
2 <{o}="Rome",{o-country}="Italy",{d}="London",{d-country}="UK">
3 <{o}="Milan",{o-country}="Italy",{d}="Paris",{d-country}="France">
4 <{o}="Rome",{o-country}="Italy",{d}="Dublin",{d-country}="Ireland">
当具体值应用于模板集时,结果将是:
"flights from Milan to Madrid"
"flights to Spain"
"flights from Milan to Spain"
"flights from Rome to London"
"flights to UK"
"flights from Rome to UK"
"flights from Milan to Paris"
"flights to France"
"flights from Milan to France"
"flights from Rome to Dublin"
"flights to Ireland"
"flights from Rome to Ireland"
假设我有大约15个参数,1,000个具体值组合和50,000个模板:最终结果将是5,000,000个字符串。
表示字符串模板的最佳方式是什么,具体的值组合是为了以最快的方式生成结果?
提前感谢大家的帮助!
答案 0 :(得分:1)
好的,有了额外的澄清,这就是我要做的事情。
首先,我将每个模板解析为一个文字数组和一个占位符字符串数组,如下所示:
"flights from {o} to {d}" ->
String [] literals = { "flights from ", " to " };
String [] placeholders = { null, "o", "d" };
literals
表示模板的文字部分,placeholders
告诉我们应该在literals
的i th 元素之前插入哪个键。 (placeholders
包含的元素多于literals
,以便我们可以处理模板以占位符开头或结尾的情况。)此步骤在初始化阶段完成一次,并且仅存储已解析的模板。
然后应将每个值组合解析为映射:
{"o":"Milan","o-country": "Italy","d":"Madrid", "d-country":"Spain" }
一旦我们以预解析的格式获得所有内容,我们就可以开始从中生成字符串了:
public String combine( String [] literals, String [] placeholders, Map<String,String> values ) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < placeholders.length; i++) {
if (placeholders[i] != null) { //a null value in the placeholders means nothing to substitute in that position
sb.append( values.get( placeholders[i] ) );
}
if (i < literals.length) { //the literals array will always be 1 shorter than the placeholder
sb.append( literals[i] );
}
}
return sb.toString();
}
或者,如果你不想去实现一个定制解决方案的麻烦,你可以使用java.text.MessageFormat
,我认为它通常是类似的,但它内部要复杂得多,因为它可以处理更复杂的模式。 :)
事实上,我可能自己从MessageFormat
开始,我只想展示它是如何在基础层面上完成的。
答案 1 :(得分:1)
如果你想编写自己的 bike 模板引擎,那么你需要的是存储预定义字符串和param名称的列表,这些名称将被实际的param值动态替换。渲染模板时,您需要迭代此列表,将参数解析为其值,并将每个部分放到StringBuilder
。
看看代码:
public class TemplateTest {
/**
* Template class. Provides method render
* that takes actual params and renders the template
*/
static class Template {
private List<TemplatePiece> pieces;
public Template(TemplatePiece... pieces) {
//defencive copy
this.pieces = new ArrayList<TemplatePiece>(Arrays.asList(pieces));
}
public String render(Map<String, String> params) {
StringBuilder result = new StringBuilder();
for (TemplatePiece piece : pieces) {
result.append(piece.render(params));
}
return result.toString();
}
}
interface TemplatePiece {
String render(Map<String, String> params);
}
static class StringTemplatePiece implements TemplatePiece {
private String string;
StringTemplatePiece(String string) {
this.string = string;
}
@Override
public String render(Map<String, String> params) {
return string;
}
}
static class ParamTemplatePiece implements TemplatePiece {
private String paramName;
ParamTemplatePiece(String paramName) {
this.paramName = paramName;
}
@Override
public String render(Map<String, String> params) {
String value = params.get(paramName);
return value == null ? "{MISSING PARAM " + paramName + "}" : value;
}
}
/**
* Create StringTemplatePiece
* Just a shortcuts for more fancy template test creation
*/
private static StringTemplatePiece s(String name) {
return new StringTemplatePiece(name);
}
/**
* Create ParamTemplatePiece
* Just a shortcuts for more fancy template test creation
*/
private static ParamTemplatePiece p(String paramName) {
return new ParamTemplatePiece(paramName);
}
public static void main(String[] args) {
Template template1 = new Template(s("flights from "), p("o"), s(" to "), p("d"));
Template template2 = new Template(s("flights to "), p("d-country"));
Template template3 = new Template(s("flights from "), p("o"), s(" to "), p("d-country"));
HashMap<String, String> params1 = new HashMap<String, String>() {{
put("o", "Milan");
put("d", "Madrid");
put("o-country", "Italy");
put("d-country", "Spain");
}};
System.out.println(template1.render(params1));
System.out.println(template2.render(params1));
System.out.println(template3.render(params1));
System.out.println();
HashMap<String, String> params2 = new HashMap<String, String>() {{
put("o", "Rome");
put("d", "London");
put("o-country", "UK");
put("d-country", "Spain");
}};
System.out.println(template1.render(params2));
System.out.println(template2.render(params2));
System.out.println(template3.render(params2));
}
}
执行结果是:
flights from Milan to Madrid
flights to Spain
flights from Milan to Spain
flights from Rome to London
flights to Spain
flights from Rome to Spain
还要考虑使用现有的模板引擎,例如Velocity。
答案 2 :(得分:0)
感谢两个人,Aivean的回答,他们确认了我的方法。
值得一提的是,我在JavaScript中为示例用例做了一些实现(请注意,每个模板行实际上包含两个单独的字符串);
var parameters = ['{d-airport-code}','{d-country}','{d}','{d-country-code}'];
var values = [
['ADD','Ethiopia','Addis Ababa','ET'],
['ADL','Australia','Adelaide','AU'],
['AMD','India','Ahmedabad','IN']
];
var templates = [
['{d-airport-code} | {d} | D','{d} air fare'],
['{d-airport-code} | {d} | D','{d} airfare'],
['{d-airport-code} | {d} | D','{d} airline ticket to'],
['{d-airport-code} | {d} | D','{d} flight']
];
/*
* Build the map of <parameter,[values]> as an object
*
* Example:
* {
* '{d-airport-code}' : ['ADD','ADL','AMD'],
* '{d-country}' : ['Ethiopia','Australia','India']
* }
*/
var combos = {};
for(var i = 0; i < parameters.length; i++) {
combos[parameters[i]] = [];
for(var j = 0; j < values.length; j++) {
combos[parameters[i]].push(values[j][i]);
}
}
/*
* Build the template representation: each template string is divided
* in segments and placeholders.
*
* Note:
* an empty string is represented as <[""],[]>
* the string "{o}" is represented as <["",""],[{o}]>
*
* In summary:
* - there is always at least a segment
* - the number of segments is always the number of placeholders + 1
*/
var template_set = [];
for(var i = 0; i < templates.length; i++) {
var template_set_row = [];
for(var j = 0; j < templates[i].length; j++) {
var segments = templates[i][j].split(/{[a-zA-Z0-9\-]+}/);
var placeholders = templates[i][j].match(/{[a-zA-Z0-9\-]+}/g);
template_set_row.push({
'segments': segments,
'placeholders': placeholders
});
/*
var template = [];
template.push(segments[0]);
for (var k = 1; k < segments.length; k++) {
template.push(combos[placeholders[k-1]]);
template.push(segments[k]);
}
template_set_row.push(template);
*/
}
template_set.push(template_set_row);
}
var result_set = [];
// For every combination of values build the string
for(var v = 0; v < values.length; v++) {
// For every template "row"
for(var i = 0; i < template_set.length; i++) {
var result_set_row = [];
// For every single template string
for(var j = 0; j < template_set[i].length; j++) {
// There is always at least one placeholder
var result = template_set[i][j].segments[0];
// The number of segments is always the number of placeholders + 1
// For every placeholder found
for(var k = 0; k < template_set[i][j].placeholders.length; k++){
result += combos[template_set[i][j].placeholders[k]][v];
result += template_set[i][j].segments[k+1];
}
result_set_row.push(result);
}
result_set.push(result_set_row);
}
}