我怎样才能取代两个string,而不是最终取代另一个?

假设我有以下代码:

String word1 = "bar"; String word2 = "foo"; String story = "Once upon a time, there was a foo and a bar." story = story.replace("foo", word1); story = story.replace("bar", word2); 

这段代码运行后, story的价值将是"Once upon a time, there was a foo and a foo."

如果我以相反的顺序replace它们,会发生类似的问题:

 String word1 = "bar"; String word2 = "foo"; String story = "Once upon a time, there was a foo and a bar." story = story.replace("bar", word2); story = story.replace("foo", word1); 

story的价值将是"Once upon a time, there was a bar and a bar."

我的目标是把story变成"Once upon a time, there was a bar and a foo." 我怎么能做到这一点?

这不是一个简单的问题。 而更多的searchreplace参数,你会得到更复杂的。 你有几种select,散布在丑陋,优雅,高效,浪费的调色板上:

  • build议使用Apache Commons中的StringUtils.replaceEach作为@AlanHay 。 如果您可以在项目中自由添加新的依赖项,这是一个不错的select。 您可能会很幸运:依赖项可能已经包含在您的项目中

  • @Jeroenbuild议使用临时占位符,并分两步执行replace:

    1. 将所有search模式replace为原始文本中不存在的唯一标记
    2. 用真正的目标replacereplace占位符

    这不是一个好方法,原因有几个:它需要确保第一步中使用的标签真的是唯一的; 它执行更多的stringreplace操作比真正的必要

  • 从所有模式构build一个正则expression式, 并按照@arshajii的build议使用MatcherStringBuffer的方法。 这并不可怕,但也不是那么好,因为构build正则expression式是一种骇人听闻的事情,而且它涉及到StringBuffer ,它不久前就开始支持StringBuilder

  • 使用@mjolka提出的recursion解决scheme,通过在匹配的模式下分割string,并recursion剩余的段。 这是一个很好的解决scheme,紧凑,相当优雅。 它的缺点是潜在的许多子串和连接操作,以及适用于所有recursion解决scheme的堆栈大小限制

  • 将文本拆分为单词,然后使用Java 8stream优雅地按照@msandiford的build议来执行replace,但是当然,只有在分隔词边界的情况下才能正常工作,这使得它不适合作为一般解决scheme

这是我的版本,基于从Apache的实施借鉴的想法。 这既不简单,也不优雅,但它的工作,应该是相对有效的,没有不必要的步骤。 简而言之,它是这样工作的:在文本中反复查找下一个匹配的search模式,并使用StringBuilder来累积不匹配的段和replace。

 public static String replaceEach(String text, String[] searchList, String[] replacementList) { // TODO: throw new IllegalArgumentException() if any param doesn't make sense //validateParams(text, searchList, replacementList); SearchTracker tracker = new SearchTracker(text, searchList, replacementList); if (!tracker.hasNextMatch(0)) { return text; } StringBuilder buf = new StringBuilder(text.length() * 2); int start = 0; do { SearchTracker.MatchInfo matchInfo = tracker.matchInfo; int textIndex = matchInfo.textIndex; String pattern = matchInfo.pattern; String replacement = matchInfo.replacement; buf.append(text.substring(start, textIndex)); buf.append(replacement); start = textIndex + pattern.length(); } while (tracker.hasNextMatch(start)); return buf.append(text.substring(start)).toString(); } private static class SearchTracker { private final String text; private final Map<String, String> patternToReplacement = new HashMap<>(); private final Set<String> pendingPatterns = new HashSet<>(); private MatchInfo matchInfo = null; private static class MatchInfo { private final String pattern; private final String replacement; private final int textIndex; private MatchInfo(String pattern, String replacement, int textIndex) { this.pattern = pattern; this.replacement = replacement; this.textIndex = textIndex; } } private SearchTracker(String text, String[] searchList, String[] replacementList) { this.text = text; for (int i = 0; i < searchList.length; ++i) { String pattern = searchList[i]; patternToReplacement.put(pattern, replacementList[i]); pendingPatterns.add(pattern); } } boolean hasNextMatch(int start) { int textIndex = -1; String nextPattern = null; for (String pattern : new ArrayList<>(pendingPatterns)) { int matchIndex = text.indexOf(pattern, start); if (matchIndex == -1) { pendingPatterns.remove(pattern); } else { if (textIndex == -1 || matchIndex < textIndex) { textIndex = matchIndex; nextPattern = pattern; } } } if (nextPattern != null) { matchInfo = new MatchInfo(nextPattern, patternToReplacement.get(nextPattern), textIndex); return true; } return false; } } 

unit testing:

 @Test public void testSingleExact() { assertEquals("bar", StringUtils.replaceEach("foo", new String[]{"foo"}, new String[]{"bar"})); } @Test public void testReplaceTwice() { assertEquals("barbar", StringUtils.replaceEach("foofoo", new String[]{"foo"}, new String[]{"bar"})); } @Test public void testReplaceTwoPatterns() { assertEquals("barbaz", StringUtils.replaceEach("foobar", new String[]{"foo", "bar"}, new String[]{"bar", "baz"})); } @Test public void testReplaceNone() { assertEquals("foofoo", StringUtils.replaceEach("foofoo", new String[]{"x"}, new String[]{"bar"})); } @Test public void testStory() { assertEquals("Once upon a foo, there was a bar and a baz, and another bar and a cat.", StringUtils.replaceEach("Once upon a baz, there was a foo and a bar, and another foo and a cat.", new String[]{"foo", "bar", "baz"}, new String[]{"bar", "baz", "foo"}) ); } 

您使用一个中间值(这不是在句子中)。

 story = story.replace("foo", "lala"); story = story.replace("bar", "foo"); story = story.replace("lala", "bar"); 

作为对批评的回应:如果使用足够大的非常string,如zq515sqdqs5d5sq1dqs4d1q5dqqé“&é5d4sqjshsjddjhodfqsqc,nvùq^μù; d&sdq:d:;)àçàçlala并使用它,那么我不会争辩用户将永远不会input这个信息,要知道用户是否知道源代码的唯一方法就是在这一点上,你还有其他的担心。

是的,也许有正则expression式的方式。 我更喜欢可读的东西,我知道也不会在我身上爆发。

同样重申@David Conrad在评论中给出的出色build议:

不要巧妙地(愚蠢地)select一些string是不太可能的。 使用Unicode专用区域U + E000..U + F8FF中的字符。 首先删除任何这样的字符,因为他们不应该合法地在input(他们只在某些应用程序中具有特定于应用程序的含义),然后在replace时使用它们作为占位符。

使用Apache Commons StringUtils中的replaceEach()方法:

 StringUtils.replaceEach(story, new String[]{"foo", "bar"}, new String[]{"bar", "foo"}) 

你可以尝试使用Matcher#appendReplacementMatcher#appendTail

 String word1 = "bar"; String word2 = "foo"; String story = "Once upon a time, there was a foo and a bar."; Pattern p = Pattern.compile("foo|bar"); Matcher m = p.matcher(story); StringBuffer sb = new StringBuffer(); while (m.find()) { /* do the swap... */ switch (m.group()) { case "foo": m.appendReplacement(sb, word1); break; case "bar": m.appendReplacement(sb, word2); break; default: /* error */ break; } } m.appendTail(sb); System.out.println(sb.toString()); 
曾几何时,有一个酒吧和一个富。

search要replace的第一个单词。 如果在string中,则在发生之前对string部分进行recursion,并在发生之后对string部分进行recursion。

否则,继续下一个单词被replace。

一个天真的实现可能看起来像这样

 public static String replaceAll(String input, String[] search, String[] replace) { return replaceAll(input, search, replace, 0); } private static String replaceAll(String input, String[] search, String[] replace, int i) { if (i == search.length) { return input; } int j = input.indexOf(search[i]); if (j == -1) { return replaceAll(input, search, replace, i + 1); } return replaceAll(input.substring(0, j), search, replace, i + 1) + replace[i] + replaceAll(input.substring(j + search[i].length()), search, replace, i); } 

示例用法:

 String input = "Once upon a baz, there was a foo and a bar."; String[] search = new String[] { "foo", "bar", "baz" }; String[] replace = new String[] { "bar", "baz", "foo" }; System.out.println(replaceAll(input, search, replace)); 

输出:

 Once upon a foo, there was a bar and a baz. 

一个不那么天真的版本:

 public static String replaceAll(String input, String[] search, String[] replace) { StringBuilder sb = new StringBuilder(); replaceAll(sb, input, 0, input.length(), search, replace, 0); return sb.toString(); } private static void replaceAll(StringBuilder sb, String input, int start, int end, String[] search, String[] replace, int i) { while (i < search.length && start < end) { int j = indexOf(input, search[i], start, end); if (j == -1) { i++; } else { replaceAll(sb, input, start, j, search, replace, i + 1); sb.append(replace[i]); start = j + search[i].length(); } } sb.append(input, start, end); } 

不幸的是,Java的String没有indexOf(String str, int fromIndex, int toIndex)方法。 我在这里省略了indexOf的实现,因为我不确定它是否正确,但是可以在ideone上find它,以及在这里发布的各种解决scheme的一些粗略的时间。

Java中的单线程8:

  story = Pattern .compile(String.format("(?<=%1$s)|(?=%1$s)", "foo|bar")) .splitAsStream(story) .map(w -> ImmutableMap.of("bar", "foo", "foo", "bar").getOrDefault(w, w)) .collect(Collectors.joining()); 
  • 查找正则expression式( ?<=?= ): http : //www.regular-expressions.info/lookaround.html
  • 如果单词可以包含特殊的正则expression式字符,使用Pattern.quote来转义它们。
  • 我使用番石榴ImmutableMap简洁,但显然任何其他地图也会做这个工作。

这是一个Java 8stream的可能性,可能会对一些有趣的:

 String word1 = "bar"; String word2 = "foo"; String story = "Once upon a time, there was a foo and a bar."; // Map is from untranslated word to translated word Map<String, String> wordMap = new HashMap<>(); wordMap.put(word1, word2); wordMap.put(word2, word1); // Split on word boundaries so we retain whitespace. String translated = Arrays.stream(story.split("\\b")) .map(w -> wordMap.getOrDefault(w, w)) .collect(Collectors.joining()); System.out.println(translated); 

以下是Java 7中的相同algorithm的近似值:

 String word1 = "bar"; String word2 = "foo"; String story = "Once upon a time, there was a foo and a bar."; // Map is from untranslated word to translated word Map<String, String> wordMap = new HashMap<>(); wordMap.put(word1, word2); wordMap.put(word2, word1); // Split on word boundaries so we retain whitespace. StringBuilder translated = new StringBuilder(); for (String w : story.split("\\b")) { String tw = wordMap.get(w); translated.append(tw != null ? tw : w); } System.out.println(translated); 

如果你想用你的例子中的空格replace一个句子中的单词,你可以使用这个简单的algorithm。

  1. 在白色空间的分裂的故事
  2. 更换每个元素,如果foo将其replace为bar和vice varsa
  3. 将数组加回到一个string中

如果在空间上分裂是不可接受的,则可以遵循这种替代algorithm。 您需要先使用较长的string。 如果这些弦是愚蠢的,那么你需要首先使用傻瓜,然后使用傻瓜。

  1. 拆分词foo
  2. 用fooreplace数组中的每个元素
  3. 除了最后一个元素之外,在数组之后join数组

这是使用Map的一个较简单的答案。

 private static String replaceEach(String str,Map<String, String> map) { Object[] keys = map.keySet().toArray(); for(int x = 0 ; x < keys.length ; x ++ ) { str = str.replace((String) keys[x],"%"+x); } for(int x = 0 ; x < keys.length ; x ++) { str = str.replace("%"+x,map.get(keys[x])); } return str; } 

和方法被调用

 Map<String, String> replaceStr = new HashMap<>(); replaceStr.put("Raffy","awesome"); replaceStr.put("awesome","Raffy"); String replaced = replaceEach("Raffy is awesome, awesome awesome is Raffy Raffy", replaceStr); 

输出是:真棒是Raffy,Raffy Raffy是真棒真棒

如果您希望能够处理要replace的多个searchstring,可以通过在每个search字词上拆分string,然后replace它来轻松完成。 这里是一个例子:

 String regex = word1 + "|" + word2; String[] values = Pattern.compile(regex).split(story); String result; foreach subStr in values { subStr = subStr.replace(word1, word2); subStr = subStr.replace(word2, word1); result += subStr; } 

你可以用下面的代码块来完成你的目标:

 String word1 = "bar"; String word2 = "foo"; String story = "Once upon a time, in a foo, there was a foo and a bar."; story = String.format(story.replace(word1, "%1$s").replace(word2, "%2$s"), word2, word1); 

不pipe顺序如何,它都会replace单词。 您可以将此原则扩展为实用方法,如下所示:

 private static String replace(String source, String[] targets, String[] replacements) throws IllegalArgumentException { if (source == null) { throw new IllegalArgumentException("The parameter \"source\" cannot be null."); } if (targets == null || replacements == null) { throw new IllegalArgumentException("Neither parameters \"targets\" or \"replacements\" can be null."); } if (targets.length == 0 || targets.length != replacements.length) { throw new IllegalArgumentException("The parameters \"targets\" and \"replacements\" must have at least one item and have the same length."); } String outputMask = source; for (int i = 0; i < targets.length; i++) { outputMask = outputMask.replace(targets[i], "%" + (i + 1) + "$s"); } return String.format(outputMask, (Object[])replacements); } 

这将被消耗为:

 String story = "Once upon a time, in a foo, there was a foo and a bar."; story = replace(story, new String[] { "bar", "foo" }, new String[] { "foo", "bar" })); 

这工作,很简单:

 public String replaceBoth(String text, String token1, String token2) { return text.replace(token1, "\ufdd0").replace(token2, token1).replace("\ufdd0", token2); } 

你这样使用它:

 replaceBoth("Once upon a time, there was a foo and a bar.", "foo", "bar"); 

注意:这个string不包含字符\ufdd0 ,它是一个永久保留供Unicode内部使用的字符(参见http://www.unicode.org/faq/private_use.html ):

我不认为这是必要的,但如果你想绝对安全,你可以使用:

 public String replaceBoth(String text, String token1, String token2) { if (text.contains("\ufdd0") || token1.contains("\ufdd0") || token2.contains("\ufdd0")) throw new IllegalArgumentException("Invalid character."); return text.replace(token1, "\ufdd0").replace(token2, token1).replace("\ufdd0", token2); } 

只交换一次发生

如果input中每个可交换string只出现一次,则可以执行以下操作:

在进行任何replace之前,请获取单词出现的索引。 之后,我们只replace在这些索引find的单词,而不是所有的事件。 这个解决scheme使用StringBuilder ,并不会像String.replace()那样产生中间的String

有一件事要注意:如果可交换的单词有不同的长度,那么第一个replace之后,第二个索引可能会改变(如果第一个单词出现在第二个之前),那么两个长度之间的差异就会完全相同。 所以alignment第二个索引将确保这个工作,即使我们交换不同长度的单词。

 public static String swap(String src, String s1, String s2) { StringBuilder sb = new StringBuilder(src); int i1 = src.indexOf(s1); int i2 = src.indexOf(s2); sb.replace(i1, i1 + s1.length(), s2); // Replace s1 with s2 // If s1 was before s2, idx2 might have changed after the replace if (i1 < i2) i2 += s2.length() - s1.length(); sb.replace(i2, i2 + s2.length(), s1); // Replace s2 with s1 return sb.toString(); } 

交换任意发生次数

类似于前面的情况,我们将首先收集单词的索引(出现次数),但在这种情况下,它将为每个单词列出整数,而不仅仅是一个int 。 为此,我们将使用以下实用程序方法:

 public static List<Integer> occurrences(String src, String s) { List<Integer> list = new ArrayList<>(); for (int idx = 0;;) if ((idx = src.indexOf(s, idx)) >= 0) { list.add(idx); idx += s.length(); } else return list; } 

使用这个,我们会用降低索引(这可能需要在两个可交换的词之间交替)来replace另一个词,这样在replace之后我们甚至不需要纠正索引:

 public static String swapAll(String src, String s1, String s2) { List<Integer> l1 = occurrences(src, s1), l2 = occurrences(src, s2); StringBuilder sb = new StringBuilder(src); // Replace occurrences by decreasing index, alternating between s1 and s2 for (int i1 = l1.size() - 1, i2 = l2.size() - 1; i1 >= 0 || i2 >= 0;) { int idx1 = i1 < 0 ? -1 : l1.get(i1); int idx2 = i2 < 0 ? -1 : l2.get(i2); if (idx1 > idx2) { // Replace s1 with s2 sb.replace(idx1, idx1 + s1.length(), s2); i1--; } else { // Replace s2 with s1 sb.replace(idx2, idx2 + s2.length(), s1); i2--; } } return sb.toString(); } 

编写一个使用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(); outer: for (int i = 0; i < subject.length(); i++) { for (int j = 0; j < pairs.length; j += 2) { String find = pairs[j]; if (subject.regionMatches(i, find, 0, find.length())) { sb.append(pairs[j + 1]); i += find.length() - 1; continue outer; } } sb.append(subject.charAt(i)); } return sb.toString(); } 

testing:

 String s = "There are three cats and two dogs."; s = simultaneousReplace(s, "cats", "dogs", "dogs", "budgies"); System.out.println(s); 

输出:

有三只狗和两只鹦鹉。

这并不是显而易见的,但是像这样的function仍然可以取决于指定replace的顺序。 考虑:

 String truth = "Java is to JavaScript"; truth += " as " + simultaneousReplace(truth, "JavaScript", "Hamster", "Java", "Ham"); System.out.println(truth); 

输出:

Java对于JavaScript来说就像火腿对仓鼠一样

但扭转替代:

 truth += " as " + simultaneousReplace(truth, "Java", "Ham", "JavaScript", "Hamster"); 

输出:

Java对于JavaScript来说就像火腿对于HamScript一样

哎呀! 🙂

因此,确保寻找最长的匹配(例如PHP的strtr函数)有时是有用的。 这个版本的方法将会这样做:

 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(); for (int i = 0; i < subject.length(); i++) { int longestMatchIndex = -1; int longestMatchLength = -1; for (int j = 0; j < pairs.length; j += 2) { String find = pairs[j]; if (subject.regionMatches(i, find, 0, find.length())) { if (find.length() > longestMatchLength) { longestMatchIndex = j; longestMatchLength = find.length(); } } } if (longestMatchIndex >= 0) { sb.append(pairs[longestMatchIndex + 1]); i += longestMatchLength - 1; } else { sb.append(subject.charAt(i)); } } return sb.toString(); } 

请注意,上述方法是区分大小写的。 如果您需要不区分大小写的版本,则可以轻松修改上述内容,因为String.regionMatches可以使用ignoreCase参数。

如果你不需要任何依赖,你可以简单地使用一个只允许一次性更改的数组。 这不是最有效的解决scheme,但它应该工作。

 public String replace(String sentence, String[]... replace){ String[] words = sentence.split("\\s+"); int[] lock = new int[words.length]; StringBuilder out = new StringBuilder(); for (int i = 0; i < words.length; i++) { for(String[] r : replace){ if(words[i].contains(r[0]) && lock[i] == 0){ words[i] = words[i].replace(r[0], r[1]); lock[i] = 1; } } out.append((i < (words.length - 1) ? words[i] + " " : words[i])); } return out.toString(); } 

那么,它会工作。

 String story = "Once upon a time, there was a foo and a bar."; String[] a = {"foo", "bar"}; String[] b = {"bar", "foo"}; String[] c = {"there", "Pocahontas"}; story = replace(story, a, b, c); System.out.println(story); // Once upon a time, Pocahontas was a bar and a foo. 

您正在input上执行多个searchreplace操作。 当replacestring包含searchstring时,这将产生不希望的结果。 考虑foo-> bar,bar-foo的例子,下面是每个迭代的结果:

  1. 曾几何时,有一个foo和一个酒吧。 (input)
  2. 曾几何时,有一个酒吧和一个酒吧。 (foo->巴)
  3. 曾几何时,有一个富有和富有。 (bar-> foo,输出)

您需要在一次迭代中执行replace,而不必返回。 蛮力解决scheme如下:

  1. search来自当前位置的input以结束多个searchstring,直到find匹配
  2. 用相应的replacestringreplace匹配的searchstring
  3. 将当前位置设置为replace后的string的下一个字符
  4. 重复

String.indexOfAny(String[]) -> int[]{index, whichString}这样的String.indexOfAny(String[]) -> int[]{index, whichString}将会很有用。 这里是一个例子(不是最有效的):

 private static String replaceEach(String str, String[] searchWords, String[] replaceWords) { String ret = ""; while (str.length() > 0) { int i; for (i = 0; i < searchWords.length; i++) { String search = searchWords[i]; String replace = replaceWords[i]; if (str.startsWith(search)) { ret += replace; str = str.substring(search.length()); break; } } if (i == searchWords.length) { ret += str.substring(0, 1); str = str.substring(1); } } return ret; } 

一些testing:

 System.out.println(replaceEach( "Once upon a time, there was a foo and a bar.", new String[]{"foo", "bar"}, new String[]{"bar", "foo"} )); // Once upon a time, there was a bar and a foo. System.out.println(replaceEach( "ap", new String[]{"a", "p"}, new String[]{"apple", "pear"} )); // apple pear System.out.println(replaceEach( "ABCDE", new String[]{"A", "B", "C", "D", "E"}, new String[]{"B", "C", "E", "E", "F"} )); // BCEEF System.out.println(replaceEach( "ABCDEF", new String[]{"ABCDEF", "ABC", "DEF"}, new String[]{"XXXXXX", "YYY", "ZZZ"} )); // XXXXXX // note the order of search strings, longer strings should be placed first // in order to make the replacement greedy 

在IDEONE上演示
演示IDEONE,备用代码

你总是可以用一个你肯定会在string中无处出现的词来replace它,然后再进行第二次replace:

 String word1 = "bar"; String word2 = "foo"; String story = "Once upon a time, there was a foo and a bar." story = story.replace("foo", "StringYouAreSureWillNeverOccur").replace("bar", "word2").replace("StringYouAreSureWillNeverOccur", "word1"); 

请注意,如果发生"StringYouAreSureWillNeverOccur" ,这将无法正常工作。

考虑使用StringBuilder

然后存储每个string应该开始的索引。 如果您在每个位置使用占位符,请将其删除,然后插入用户string。 然后可以通过将string长度添加到起始位置来映射结束位置。

 String firstString = "???"; String secondString = "???" StringBuilder story = new StringBuilder("One upon a time, there was a " + firstString + " and a " + secondString); int firstWord = 30; int secondWord = firstWord + firstString.length() + 7; story.replace(firstWord, firstWord + firstString.length(), userStringOne); story.replace(secondWord, secondWord + secondString.length(), userStringTwo); firstString = userStringOne; secondString = userStringTwo; return story; 

What I can only share is my own method.

You can use a temporary String temp = "<?>"; or String.Format();

This is my example code created in console application via c# -"Idea Only, Not Exact Answer" .

 static void Main(string[] args) { String[] word1 = {"foo", "Once"}; String[] word2 = {"bar", "time"}; String story = "Once upon a time, there was a foo and a bar."; story = Switcher(story,word1,word2); Console.WriteLine(story); Console.Read(); } // Using a temporary string. static string Switcher(string text, string[] target, string[] value) { string temp = "<?>"; if (target.Length == value.Length) { for (int i = 0; i < target.Length; i++) { text = text.Replace(target[i], temp); text = text.Replace(value[i], target[i]); text = text.Replace(temp, value[i]); } } return text; } 

Or you can also use the String.Format();

 static string Switcher(string text, string[] target, string[] value) { if (target.Length == value.Length) { for (int i = 0; i < target.Length; i++) { text = text.Replace(target[i], "{0}").Replace(value[i], "{1}"); text = String.Format(text, value[i], target[i]); } } return text; } 

Output: time upon a Once, there was a bar and a foo.

Here's my version, which is word-based:

 class TextReplace { public static void replaceAll (String text, String [] lookup, String [] replacement, String delimiter) { String [] words = text.split(delimiter); for (int i = 0; i < words.length; i++) { int j = find(lookup, words[i]); if (j >= 0) words[i] = replacement[j]; } text = StringUtils.join(words, delimiter); } public static int find (String [] array, String key) { for (int i = 0; i < array.length; i++) if (array[i].equals(key)) return i; return (-1); } } 
 String word1 = "bar"; String word2 = "foo"; String story = "Once upon a time, there was a foo and a bar." 

Little tricky way but you need to do some more checks.

1.convert string to character array

  String temp[] = story.split(" ");//assume there is only spaces. 

2.loop on temp and replace foo with bar and bar with foo as there are no chances of getting replaceable string again.

Well, the shorter answer is…

 String word1 = "bar"; String word2 = "foo"; String story = "Once upon a time, there was a foo and a bar."; story = story.replace("foo", "@"+ word1).replace("bar", word2).replace("@" + word2, word1); System.out.println(story); 

Using the answer found here you can find all occurrences of the strings you wish to replace with.

So for example you run the code in the above SO answer. Create two tables of indexes (let's say bar and foo do not appear only once in your string) and you can work with those tables on replacing them in your string.

Now for replacing on specific index locations you can use:

 public static String replaceStringAt(String s, int pos, String c) { return s.substring(0,pos) + c + s.substring(pos+1); } 

Whereas pos is the index where your strings start (from the index tables I quoted above). So let's say you created two tables of indexes for each one. Let's call them indexBar and indexFoo .

Now in replacing them you could simply run two loops, one for each replacements you wish to make.

 for(int i=0;i<indexBar.Count();i++) replaceStringAt(originalString,indexBar[i],newString); 

Similarly another loop for indexFoo .

This may not be as efficient as other answers here but it's simpler to understand than Maps or other stuff.

This would always give you the result you wish and for multiple possible occurrences of each string. As long as you store the index of each occurrence.

Also this answer needs no recursion nor any external dependencies. As far as complexity goes it propably is O(n squared), whereas n is the sum of occurences of both words.

I developed this code will solve problem:

 public static String change(String s,String s1, String s2) { int length = s.length(); int x1 = s1.length(); int x2 = s2.length(); int x12 = s.indexOf(s1); int x22 = s.indexOf(s2); String s3=s.substring(0, x12); String s4 =s.substring(x12+3, x22); s=s3+s2+s4+s1; return s; } 

In the main use change(story,word2,word1).

 String word1 = "bar"; String word2 = "foo"; String story = "Once upon a time, there was a foo and a bar." story = story.replace("foo", "<foo />"); story = story.replace("bar", "<bar />"); story = story.replace("<foo />", word1); story = story.replace("<bar />", word2);