In a programmer's work, quite often some tasks or their components may be repeated. Therefore, today I would like to touch on a topic that is often encountered in the daily work of any Java developer. Let's assume that you receive a certain string from a certain method. And everything about it seems to be good, but there is some little thing that doesn’t suit you. For example, the separator is not suitable, and you need some other one (or not at all). What can be done in such a situation? Naturally, use the methods
replace
of the class String
.
Java string replace
The type objectString
has four variations of the replacement method replace
:
replace(char, char);
replace(CharSequence, CharSequence);
replaceFirst(String, String);
replaceAll(String, String).
replace(char, char)
String replace(char oldChar, char newChar)
- replaces all occurrences of the character of the first argument oldChar
with the second - newChar
. In this example, we'll replace the comma with a semicolon:
String value = "In JavaRush, Diego the best, Diego is Java God".replace(',', ';');
System.out.println(value);
Console output:
In JavaRush; Diego the best; Diego is Java God
2.replace(CharSequence, CharSequence)
Replaces each substring of a string that matches a specified sequence of characters with a sequence of replacement characters.
String value = "In JavaRush, Diego the best, Diego is Java God".replace("Java", "Rush");
System.out.println(value);
Conclusion:
In RushRush, Diego the best, Diego is Rush God
3.replaceFirst(String, String)
String replaceFirst(String regex, String replacement)
- Replaces the first substring that matches the specified regular expression with a replacement string. When using an invalid regular expression, you can catch a PatternSyntaxException (which is not a good thing). In this example, let's replace the name of the champion robot:
String value = "In JavaRush, Diego the best, Diego is Java God".replaceFirst("Diego", "Amigo");
System.out.println(value);
Console output:
In JavaRush, Amigo the best, Diego is Java God
As we can see, only the first entry of “Diego” has changed, but the subsequent ones remained left out—that is, untouched. 4. replaceAll()
in Java String replaceAll(String regex, String replacement)
- this method replaces all occurrences of a substring in a string regex
with replacement
. A regular expression can be used as the first argument regex
. As an example, let's try to perform the previous replacement with names, but with a new method:
String value = "In JavaRush, Diego the best, Diego is Java God".replaceAll("Diego", "Amigo");
System.out.println(value);
Console output:
In JavaRush, Amigo the best, Amigo is Java God
As we can see, all symbols have been completely replaced with the necessary ones. I think Amigo will be pleased =)
Regular Expressions
It was said above that it is possible to replace using a regular expression. First, let's clarify for ourselves what a regular expression is? Regular expressions are a formal language for searching and manipulating substrings in text, based on the use of metacharacters (wildcards). Simply put, it is a pattern of characters and metacharacters that defines a search rule. For example:\D
- a template describing any non-digital character; \d
— defines any numeric character, which can also be described as [0-9]
; [a-zA-Z]
— a template describing Latin characters from a to z, case insensitive; Consider the application in a replaceAll
class method String
:
String value = "In JavaRush, Diego the best, Diego is Java God".replaceAll("\\s[a-zA-Z]{5}\\s", " Amigo ");
System.out.println(value);
Console output:
In JavaRush, Amigo the best, Amigo is Java God
\\s[a-zA-Z]{5}\\s
— describes a word of 5 Latin characters surrounded by spaces. Accordingly, this template is replaced with the string we passed.
Java regex replace
Basically, to use regular expressions in Java, the capabilities of thejava.util.regex
. The key classes are:
Pattern
- a class that provides a compiled version of a regular expression.Matcher
— this class interprets the pattern and determines matches in the string it receives.
Matcher
and Pattern
:
Pattern pattern = Pattern.compile("\\s[a-zA-Z]{5}\\s");
Matcher matcher = pattern.matcher("In JavaRush, Diego the best, Diego is Java God");
String value = matcher.replaceAll(" Amigo ");
System.out.println(value);
And our conclusion will be the same:
In JavaRush, Amigo the best, Amigo is Java God
You can read more about regular expressions in this article .
Alternative to replaceAll
There is no doubt that the methodsreplace
are String
very impressive, but one cannot ignore the fact that String
it is immutable
an object, that is, it cannot be changed after its creation. Therefore, when we replace some parts of a string using methods replace
, we do not change the object String
, but create a new one each time, with the necessary contents. But it takes a long time to create a new object every time, doesn’t it? Especially when the question is not a couple of objects, but a couple of hundreds, or even thousands. Willy-nilly, you begin to think about alternatives. And what alternatives do we have? Hmm... When it comes to String
its property immutable
, you immediately think of alternatives, but not immutable
, namely StringBuilder/StringBuffer . As we remember, these classes do not actually differ, except that StringBuffer
they are optimized for use in a multi-threaded environment, so StringBuilder
they work somewhat faster in single-threaded use. Based on this, today we will use StringBuilder.
This class has many interesting methods, but specifically now we are interested in replace
. StringBuilder replace(int start, int end, String str)
— this method replaces characters in a substring of this sequence with characters in the specified string. The substring begins at the specified beginning and continues until the character at the end of the index, -1
or until the end of the sequence if no such character exists. Let's look at an example:
StringBuilder strBuilder = new StringBuilder("Java Rush");
strBuilder.replace(5, 9, "God");
System.out.println(strBuilder);
Conclusion:
Java God
As you can see, we indicate the interval in which we want to write the string, and write a substring on top of what is in the interval. So, using the help, StringBuilder
we will recreate an analogue of the method replaceall java
. How will it look like:
public static String customReplaceAll(String str, String oldStr, String newStr) {
if ("".equals(str) || "".equals(oldStr) || oldStr.equals(newStr)) {
return str;
}
if (newStr == null) {
newStr = "";
}
final int strLength = str.length();
final int oldStrLength = oldStr.length();
StringBuilder builder = new StringBuilder(str);
for (int i = 0; i < strLength; i++) {
int index = builder.indexOf(oldStr, i);
if (index == -1) {
if (i == 0) {
return str;
}
return builder.toString();
}
builder = builder.replace(index, index + oldStrLength, newStr);
}
return builder.toString();
}
It’s scary at first glance, but with a little understanding you can understand that everything is not so complicated and quite logical. We have three arguments:
str
— a line in which we want to replace some substrings;oldStr
— representation of substrings that we will replace;newStr
- what we will replace it with.
if
We need the first one to check the incoming data, and if the string str
is either oldStr
empty, or the new substring newStr
is equal to the old one oldStr
, then executing the method will be meaningless. Therefore, we return the original string - str
. Next, we check newStr
for null
, and if this is the case, then we convert it into an empty string format that is more convenient for us - ""
. Then we have the declaration of the variables we need:
- total string length
str
; - substring length
oldStr
; - object
StringBuilder
from a shared string.
StringBuilder
- indexOf
- we find out the index of the first occurrence of the substring we are interested in. Unfortunately, I would like to note that it indexOf
does not work with regular expressions, so our final method will only work with occurrences of strings (( If this index is equal to -1
, then there are no more occurrences of these occurrences in the current object StringBuilder
, so we exit the method with the result of interest: it is contained in our StringBuilder
, which we convert to String
, using toString
. If our index is equal -1
in the first iteration of the loop, then the substring that needs to be replaced was not in the general string initially. Therefore, in such a situation, we simply return the general string. Next we have and the method described above is used replace
for StringBuilder
using the found index of the occurrence to indicate the coordinates of the substring to be replaced. This loop will run as many times as substrings that need to be replaced are found. If the string consists only of the character that needs to be replaced, then only in this case we have The loop will run completely and we will get the result StringBuilder
converted to a string. We need to check the correctness of this method, right? Let's write a test that checks the operation of the method in various situations:
@Test
public void customReplaceAllTest() {
String str = "qwertyuiop__qwertyuiop__";
String firstCase = Solution.customReplaceAll(str, "q", "a");
String firstResult = "awertyuiop__awertyuiop__";
assertEquals(firstCase, firstResult);
String secondCase = Solution.customReplaceAll(str, "q", "ab");
String secondResult = "abwertyuiop__abwertyuiop__";
assertEquals(secondCase, secondResult);
String thirdCase = Solution.customReplaceAll(str, "rtyu", "*");
String thirdResult = "qwe*iop__qwe*iop__";
assertEquals(thirdCase, thirdResult);
String fourthCase = Solution.customReplaceAll(str, "q", "");
String fourthResult = "wertyuiop__wertyuiop__";
assertEquals(fourthCase, fourthResult);
String fifthCase = Solution.customReplaceAll(str, "uio", "");
String fifthResult = "qwertyp__qwertyp__";
assertEquals(fifthCase, fifthResult);
String sixthCase = Solution.customReplaceAll(str, "", "***");
assertEquals(sixthCase, str);
String seventhCase = Solution.customReplaceAll("", "q", "***");
assertEquals(seventhCase, "");
}
Can be divided into 7 separate tests, each of which will be responsible for its own test case. Having launched it, we will see that it is green, that is, successful. Well, that seems to be all. Although wait, we said above that this method will be much faster replaceAll
than String
. Well, let's see:
String str = "qwertyuiop__qwertyuiop__";
long firstStartTime = System.nanoTime();
for (long i = 0; i < 10000000L; i++) {
str.replaceAll("tyu", "#");
}
double firstPerformance = System.nanoTime() - firstStartTime;
long secondStartTime = System.nanoTime();
for (long i = 0; i < 10000000L; i++) {
customReplaceAll(str, "tyu", "#");
}
double secondPerformance = System.nanoTime() - secondStartTime;
System.out.println("Performance ratio - " + firstPerformance / secondPerformance);
Next, this code was run three times and we got the following results: Console output:
Performance ratio - 5.012148941181627
Performance ratio - 5.320637176017641
Performance ratio - 4.719192686500394
As we can see, on average our method is 5 times more productive than the classic replaceAll
class ! String
Well, finally, let's run the same check, but, so to speak, in vain. In other words, in the case when no match is found. Let's replace the search string from "tyu"
to "--"
. Three runs yielded the following results: Console output:
Performance ratio - 8.789647093542246
Performance ratio - 9.177105482660881
Performance ratio - 8.520964375227406
On average, performance for cases where no matches were found increased by 8.8 times!
GO TO FULL VERSION