プログラマーの作業では、一部のタスクまたはそのコンポーネントが繰り返されることがよくあります。そこで、今日は Java 開発者の日常業務でよく遭遇するトピックについて触れたいと思います。 特定のメソッドから特定の文字列を受け取ったと仮定します。すべてがうまくいっているように見えますが、少し気に入らない点がいくつかあります。たとえば、セパレータが適切ではないため、別のセパレータが必要です (またはまったく必要ありません)。このような状況では何ができるでしょうか?
replace
当然、クラスのメソッドを使用しますString
。
Java 文字列置換
型オブジェクトには、String
置換メソッドの 4 つのバリエーションがありますreplace
。
replace(char, char);
replace(CharSequence, CharSequence);
replaceFirst(String, String);
replaceAll(String, String).
replace(char, char)
String replace(char oldChar, char newChar)
- は、最初の引数の文字のすべての出現をoldChar
2 番目の - に置き換えますnewChar
。この例では、コンマをセミコロンに置き換えます。
String value = "In JavaRush, Diego the best, Diego is Java God".replace(',', ';');
System.out.println(value);
コンソール出力:
In JavaRush; Diego the best; Diego is Java God
2.replace(CharSequence, CharSequence)
指定された文字シーケンスに一致する文字列の各部分文字列を、一連の置換文字で置き換えます。
String value = "In JavaRush, Diego the best, Diego is Java God".replace("Java", "Rush");
System.out.println(value);
結論:
In RushRush, Diego the best, Diego is Rush God
3.replaceFirst(String, String)
String replaceFirst(String regex, String replacement)
- 指定された正規表現に一致する最初の部分文字列を置換文字列に置き換えます。無効な正規表現を使用すると、PatternSyntaxException が発生する可能性があります(これは良いことではありません)。この例では、チャンピオン ロボットの名前を置き換えてみましょう。
String value = "In JavaRush, Diego the best, Diego is Java God".replaceFirst("Diego", "Amigo");
System.out.println(value);
コンソール出力:
In JavaRush, Amigo the best, Diego is Java God
ご覧のとおり、「Diego」の最初のエントリのみが変更されていますが、後続のエントリは省略されたまま、つまり変更されていません。 4. replaceAll()
Java の場合 String replaceAll(String regex, String replacement)
- このメソッドは、文字列内のすべての部分文字列をregex
に置き換えますreplacement
。第一引数には正規表現を使用できますregex
。例として、以前の名前の置換を新しいメソッドで実行してみます。
String value = "In JavaRush, Diego the best, Diego is Java God".replaceAll("Diego", "Amigo");
System.out.println(value);
コンソール出力:
In JavaRush, Amigo the best, Amigo is Java God
ご覧のとおり、すべてのシンボルが必要なシンボルに完全に置き換えられています。アミーゴもきっと喜んでくれると思いますよ=)
正規表現
正規表現を使用して置換できることは上で述べました。まず、正規表現とは何なのかを自分自身で明確にしてみましょう。 正規表現は、メタ文字 (ワイルドカード) の使用に基づいて、テキスト内の部分文字列を検索および操作するための形式的な言語です。簡単に言えば、検索ルールを定義する文字とメタキャラクターのパターンです。例:\D
- 非デジタル文字を説明するテンプレート。 \d
— 任意の数字を定義します。これは[0-9]
; としても記述できます。[a-zA-Z]
— a から z までのラテン文字を記述したテンプレート (大文字と小文字は区別されません)。replaceAll
クラスメソッド内のアプリケーションを考えてみましょうString
。
String value = "In JavaRush, Diego the best, Diego is Java God".replaceAll("\\s[a-zA-Z]{5}\\s", " Amigo ");
System.out.println(value);
コンソール出力:
In JavaRush, Amigo the best, Amigo is Java God
\\s[a-zA-Z]{5}\\s
— スペースで囲まれた 5 つのラテン文字の単語を表します。したがって、このテンプレートは渡した文字列に置き換えられます。
Java正規表現の置換
基本的に、Java で正規表現を使用するには、java.util.regex
. 主要なクラスは次のとおりです。
Pattern
- 正規表現のコンパイルされたバージョンを提供するクラス。Matcher
— このクラスはパターンを解釈し、受け取った文字列内の一致を判断します。
Matcher
それでは、との助けを借りて、前のオブジェクトはどのように見えるでしょうか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);
そして結論は同じになります:
In JavaRush, Amigo the best, Amigo is Java God
正規表現について詳しくは、この記事を ご覧ください。
replaceAll の代替案
replace
これらのメソッドが非常に印象的であることは間違いありませんが、それがオブジェクトである、つまり作成後に変更できないというString
事実を無視することはできません。したがって、メソッドを使用して文字列の一部を置き換える場合、オブジェクトは変更されませんが、必要な内容を含む毎回新しいオブジェクトが作成されます。でも、毎回新しいオブジェクトを作成するのは時間がかかりますよね。特に、問題が数個のオブジェクトではなく、数百、あるいは数千のオブジェクトである場合にはなおさらです。当然のことながら、あなたは代替案を考え始めます。そして、どのような代替手段があるのでしょうか? うーん...プロパティのことになると、すぐに代替案が思い浮かびますが、そうではありません。それは、StringBuilder/StringBufferです。覚えているとおり、これらのクラスは、マルチスレッド環境での使用に最適化されているため、シングルスレッドでの使用の方が若干高速に動作することを除いて、実際には違いはありません。これに基づいて、今日は このクラスには多くの興味深いメソッドがありますが、特に今興味があるのは です。 — このメソッドは、このシーケンスの部分文字列内の文字を、指定された文字列内の文字に置き換えます。部分文字列は、指定された先頭から始まり、インデックスの末尾の文字まで、またはそのような文字が存在しない場合はシーケンスの末尾まで続きます。例を見てみましょう: String
immutable
replace
String
String
immutable
immutable
StringBuffer
StringBuilder
StringBuilder.
replace
StringBuilder replace(int start, int end, String str)
-1
StringBuilder strBuilder = new StringBuilder("Java Rush");
strBuilder.replace(5, 9, "God");
System.out.println(strBuilder);
結論:
Java God
ご覧のとおり、文字列を書き込む間隔を指定し、その間隔内にあるものの上に部分文字列を書き込みます。そこで、ヘルプを使用して、StringBuilder
メソッドの類似物を再作成しますreplaceall java
。それは次のようになります:
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();
}
一見すると恐ろしいですが、少し理解すれば、すべてがそれほど複雑ではなく、非常に論理的であることが理解できます。
str
— 一部の部分文字列を置換する行。oldStr
— 置換する部分文字列の表現。newStr
- それを何に置き換えるか。
if
必要ですが、文字列str
がoldStr
空であるか、新しい部分文字列がnewStr
古い部分文字列と等しいoldStr
場合、メソッドを実行するのは無意味です。したがって、元の文字列 - を返しますstr
。次に、 をチェックしnewStr
、null
そうであれば、それをより便利な空の文字列形式に変換します""
。次に、必要な変数の宣言があります。
- 文字列の合計長
str
。 - 部分文字列の長さ
oldStr
; StringBuilder
共有文字列からのオブジェクト。
StringBuilder
関心indexOf
のある部分文字列が最初に出現するインデックスを見つけます。残念ながら、これは正規表現では機能しないことに注意してくださいindexOf
。そのため、最後のメソッドは文字列 (( このインデックスが に等しい場合-1
、現在のオブジェクトにはこれらの出現は存在しません) の出現でのみ機能しますStringBuilder
。したがって、関心のある結果でメソッドを終了します: これは に含まれておりStringBuilder
、 をString
使用してに変換しますtoString
。ループの最初の反復でインデックスが等しい場合-1
、置換する必要がある部分文字列は一般的なものではありません。最初は文字列です。したがって、そのような状況では、単純に一般的な文字列を返します。次に、上で説明したメソッドを使用して、replace
見つかっStringBuilder
た出現のインデックスを使用して、置換される部分文字列の座標を示します。このループが実行されます。置換する必要がある部分文字列が見つかった回数だけ、文字列が置換する必要のある文字のみで構成されている場合、この場合にのみループが完全に実行され、結果が文字列に変換されますStringBuilder
。この方法が正しいかどうかを確認する必要がありますね? さまざまな状況でメソッドの動作をチェックするテストを書いてみましょう。
@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, "");
}
7 つの個別のテストに分割でき、それぞれが独自のテスト ケースを担当します。起動すると、緑色、つまり成功したことがわかります。まあ、それだけのようです。お待ちくださいが、この方法はreplaceAll
よりもはるかに高速であると上で述べましたString
。さて、見てみましょう:
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);
次に、このコードが 3 回実行され、次の結果が得られました。 コンソール出力:
Performance ratio - 5.012148941181627
Performance ratio - 5.320637176017641
Performance ratio - 4.719192686500394
ご覧のとおり、平均して、私たちのメソッドは従来のreplaceAll
クラスよりもString
5 倍生産性が高くなります。さて、最後に同じチェックを実行してみますが、いわば無駄です。つまり、一致するものが見つからなかった場合です。検索文字列を から"tyu"
に置き換えてみましょう"--"
。3 回実行すると、次の結果が得られました。 コンソール出力:
Performance ratio - 8.789647093542246
Performance ratio - 9.177105482660881
Performance ratio - 8.520964375227406
平均して、一致が見つからなかった場合のパフォーマンスは 8.8 倍に向上しました。
GO TO FULL VERSION