First, a demonstration of the problem:
String s = "I have three cats and two dogs."; s = s.replace("cats", "dogs") .replace("dogs", "budgies"); System.out.println(s);
This is intended to replace cats => dogs and dogs => budgets, but a sequential change works on the result of the previous replacement, so the unsuccessful conclusion:
I have three budgies and two budgies.
Here is my implementation of the simultaneous replacement method. Easy to write with String.regionMatches :
public static String simultaneousReplace(String subject, String... pairs) { if (pairs.length % 2 != 0) throw new IllegalArgumentException( "Strings to find and replace are not paired."); StringBuilder sb = new StringBuilder(); int numPairs = pairs.length / 2; outer: for (int i = 0; i < subject.length(); i++) { for (int j = 0; j < numPairs; j++) { String find = pairs[j * 2]; if (subject.regionMatches(i, find, 0, find.length())) { sb.append(pairs[j * 2 + 1]); i += find.length() - 1; continue outer; } } sb.append(subject.charAt(i)); } return sb.toString(); }
Testing:
String s = "I have three cats and two dogs."; s = simultaneousReplace(s, "cats", "dogs", "dogs", "budgies"); System.out.println(s);
Output:
I have three dogs and two friends.
In addition, it is sometimes useful while replacing at the same time to make sure that you are looking for the longest match. (The PHP strtr function does this, for example.) Here is my implementation for this:
public static String simultaneousReplaceLongest(String subject, String... pairs) { if (pairs.length % 2 != 0) throw new IllegalArgumentException( "Strings to find and replace are not paired."); StringBuilder sb = new StringBuilder(); int numPairs = pairs.length / 2; for (int i = 0; i < subject.length(); i++) { int longestMatchIndex = -1; int longestMatchLength = -1; for (int j = 0; j < numPairs; j++) { String find = pairs[j * 2]; if (subject.regionMatches(i, find, 0, find.length())) { if (find.length() > longestMatchLength) { longestMatchIndex = j; longestMatchLength = find.length(); } } } if (longestMatchIndex >= 0) { sb.append(pairs[longestMatchIndex * 2 + 1]); i += longestMatchLength - 1; } else { sb.append(subject.charAt(i)); } } return sb.toString(); }
Why do you need this? Example:
String truth = "Java is to JavaScript"; truth += " as " + simultaneousReplaceLongest(truth, "Java", "Ham", "JavaScript", "Hamster"); System.out.println(truth);
Output:
Java is JavaScript because Ham is Hamster
If we used simultaneousReplace instead of simultaneousReplaceLongest , the output would be "HamScript" instead of "Hamster :)
Please note that the above methods are case sensitive. If you want case-insensitive versions, this is easy to change, because String.regionMatches can accept the ignoreCase parameter.