ReplaceAll with java8 lambda functions
import java.util.HashMap;
import java.util.Map;
public class Repl {
public static void main(String[] args) {
Map<String, String> variables = new HashMap<>();
String templateText = "Hi, ${name} ${secondname}! My name is ${name} too :)";
variables.put("name", "Joe");
variables.put("secondname", "White");
templateText = variables.keySet().stream().reduce(templateText, (acc, e) -> acc.replaceAll("\\$\\{" + e + "\\}", variables.get(e)));
System.out.println(templateText);
}
}
output:
Hi, Joe White! My name is Joe too :)
However, it's not the best idea to reinvent the wheel and the preferred way to achieve what you want would be to use apache commons lang as stated here.
Map<String, String> valuesMap = new HashMap<String, String>();
valuesMap.put("animal", "quick brown fox");
valuesMap.put("target", "lazy dog");
String templateString = "The ${animal} jumped over the ${target}.";
StrSubstitutor sub = new StrSubstitutor(valuesMap);
String resolvedString = sub.replace(templateString);
you also can using Stream.reduce(identity,accumulator,combiner).
identity
identity
is the initial value for reducing function which is accumulator
.
accumulator
accumulator
reducing identity
to result
, which is the identity
for the next reducing if the stream is sequentially.
combiner
this function never be called in sequentially stream. it calculate the next identity
from identity
& result
in parallel stream.
BinaryOperator<String> combinerNeverBeCalledInSequentiallyStream=(identity,t) -> {
throw new IllegalStateException("Can't be used in parallel stream");
};
String result = variables.entrySet().stream()
.reduce(templateText
, (it, var) -> it.replaceAll(format("\\$\\{%s\\}", var.getKey())
, var.getValue())
, combinerNeverBeCalledInSequentiallyStream);
Java 8
The proper way to implement this has not changed in Java 8, it is based on appendReplacement()
/appendTail()
:
Pattern variablePattern = Pattern.compile("\\$\\{(.+?)\\}");
Matcher matcher = variablePattern.matcher(templateText);
StringBuffer result = new StringBuffer();
while (matcher.find()) {
matcher.appendReplacement(result, variables.get(matcher.group(1)));
}
matcher.appendTail(result);
System.out.println(result);
Note that, as mentioned by drrob in the comments, the replacement String of appendReplacement()
may contain group references using the $
sign, and escaping using \
. If this is not desired, or if your replacement String can potentially contain those characters, you should escape them using Matcher.quoteReplacement()
.
Being more functional in Java 8
If you want a more Java-8-style version, you can extract the search-and-replace boiler plate code into a generalized method that takes a replacement Function
:
private static StringBuffer replaceAll(String templateText, Pattern pattern,
Function<Matcher, String> replacer) {
Matcher matcher = pattern.matcher(templateText);
StringBuffer result = new StringBuffer();
while (matcher.find()) {
matcher.appendReplacement(result, replacer.apply(matcher));
}
matcher.appendTail(result);
return result;
}
and use it as
Pattern variablePattern = Pattern.compile("\\$\\{(.+?)\\}");
StringBuffer result = replaceAll(templateText, variablePattern,
m -> variables.get(m.group(1)));
Note that having a Pattern
as parameter (instead of a String
) allows it to be stored as a constant instead of recompiling it every time.
Same remark applies as above concerning $
and \
– you may want to enforce the quoteReplacement()
inside the replaceAll()
method if you don't want your replacer
function to handle it.
Java 9 and above
Java 9 introduced Matcher.replaceAll(Function)
which basically implements the same thing as the functional version above. See Jesse Glick's answer for more details.