-1

I am using Java 1.8. I have a large amount of text in a buffer. The text has some occurrences likt the following:

"... {NAME} is going to {PLACE}...", blah blah blah.

Then I have two arrays: "{NAME};{PLACE}" and "Mick Jagger;A Gogo", etc. (These are just examples). I make a Map replacements of these such as

 {NAME};Mick Jagger
 {PLACE};A Gogo

So I want to do all the replacements. In this case there is only 2 so it is not so cumbersome. Say my original text is in txt:

 for (EntrySet<String, String> entry : replacements.entrySet()) {
   txt = txt.replace(entry.getKey(), entry.getValue());
 }

You can imaging if there are like a lot of replacements this could take a long time.

Is there some better way to make all the replacements, or is this basically what you would do?

Tony
  • 1,127
  • 1
  • 18
  • 29
  • It looks that your problem could be resolved using a template engine. There are many of them, maybe a good candidate is Handlebars, a Mustache implementation for Java, where you can change the default delimiter to use your own. https://github.com/jknack/handlebars.java – Ezequiel Nov 05 '19 at 16:04

2 Answers2

0

I'd suggest reading file line by line (using NIO) and for each line you can iterate your map and replace it if you have something. So in this case you need to go over your big data only once

Vasif
  • 668
  • 4
  • 10
  • This might use less memory if the files are large (so long as there are no very long lines), but I don't expect it to be significantly faster, since you're still doing the same number of calls to `.replace` per character. The complexity is still O(*nk*) where *n* is the length of the string and *k* is the number of replacement pairs. – kaya3 Nov 05 '19 at 14:14
  • Did you checked this nice tutorial? http://tutorials.jenkov.com/java-howto/replace-strings-in-streams-arrays-files.html – Vasif Nov 05 '19 at 14:27
0

To avoid calling String.replace many times, you can use a regex which matches every replacement-key. You can then iteratively scan for the next replacement-key in the input string using a loop, and find its substitute using Map.get:

import java.util.Collection;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

public class ReplaceMap {
    private final Pattern p;

    public ReplaceMap(Collection<String> keys) {
        this.p = Pattern.compile(keys.stream()
            .map(ReplaceMap::escapeRegex)
            .collect(Collectors.joining("|")));
    }

    public String replace(String input, Map<String,String> subs) {
        Matcher m = p.matcher(input);
        StringBuilder out = new StringBuilder();
        int i = 0;
        while(m.find()) {
            out.append(input.substring(i, m.start()));
            String key = m.group(0);
            out.append(subs.get(key));
            i = m.end();
        }
        out.append(input.substring(i));
        return out.toString();
    }

    // from https://stackoverflow.com/a/25853507/12299000
    private static Pattern SPECIAL_REGEX_CHARS = Pattern.compile("[{}()\\[\\].+*?^$\\\\|]");
    private static String escapeRegex(String s) {
        return SPECIAL_REGEX_CHARS.matcher(s).replaceAll("\\\\$0");
    }
}

Usage:

> Map<String,String> subs = new HashMap<>();
> subs.put("{NAME}", "Alice");
> subs.put("{PLACE}", "Wonderland");
> ReplaceMap r = new ReplaceMap(subs.keySet());
> r.replace("Hello, {NAME} in {PLACE}.", subs)
"Hello, Alice in Wonderland." (String)

This solution should be about as efficient regardless of how many replacement key/value pairs there are in the subs map.

kaya3
  • 47,440
  • 4
  • 68
  • 97