Javaチャレンジャー#2:文字列の比較
いつものように、コースの開始にはかなり遅れているので、昨日、新しいスレッド「Java Developer」の間で2回目のレッスンを開催しました。 しかし、これは人生のささいなことですが、今のところ、Java Challengersの一連の記事を公開し続けています。
Javaでは、 String
クラスはchar
配列をカプセル化します( 翻訳者のメモ-Java 9では既にbyte
配列になっています。Java9の文字列のコンパクト化を参照 )。 簡単に言えば、 String
は、単語、文、またはその他の構成要素を構成するために使用される文字の配列です。
カプセル化は、オブジェクト指向プログラミングの最も強力な概念の1つです。 カプセル化のおかげで、 String
クラスの仕組みを知る必要はありません。 インターフェースのメソッドを知る必要があるだけです。

JavaのString
クラスを見ると、 char
配列がどのようにカプセル化されているかがわかります。
public String(char value[]) { this(value, 0, value.length, null); }
カプセル化をよりよく理解するために、物理オブジェクトであるマシンを想像してください。 車を運転するには、ボンネットの下で車がどのように機能するかを知る必要がありますか? もちろんそうではありませんが、車のインターフェースが何をしているのかを知っておく必要があります:アクセルペダル、ブレーキ、ハンドル。 これらの各インターフェイスは、特定のアクションをサポートしています:加速、ブレーキ、左折、右折。 オブジェクト指向プログラミングでも同じことが言えます。
Java Challengersシリーズの最初の記事はメソッドのオーバーロードに関するもので、 String
クラスで広く使用されています。 オーバーロードにより、クラスが非常に柔軟になります。
public String(String original) {} public String(char value[], int offset, int count) {} public String(int[] codePoints, int offset, int count) {} public String(byte bytes[], int offset, int length, String charsetName) {}
この記事では、 String
クラスの仕組みを理解しようとする代わりに、その機能とコードでの使用方法を理解するのに役立ちます。
文字列プールとは何ですか?
String
クラスは、おそらくJavaで最も一般的に使用されるクラスです。 String
を使用するたびに動的メモリ(メモリヒープ)に新しいオブジェクトを作成すると、大量のメモリが無駄になります。 文字列プールは、各行の値に対して1つのオブジェクトのみを保存することでこの問題を解決します。

行プールの行
値Duke
およびJuggy
を使用していくつかのString
変数を作成しましたが、2つのオブジェクトのみが作成され、動的メモリ(ヒープ)に格納されます。 証明については、次のコード例を参照してください。 (Javaでは、「 ==
」演算子を使用して2つのオブジェクトを比較し、同じオブジェクトが同じかどうかを判断します。)
String juggy = "Juggy"; String anotherJuggy = "Juggy"; System.out.println(juggy == anotherJuggy);
2つのString
変数が文字列プール内の同じオブジェクトを指しているため、このコードはtrue
を返します。 それらの意味は同じです。
例外はnew
演算子です。
このコードを見てください。前の例と似ていますが、違いがあります。
String duke = new String("duke"); String anotherDuke = new String("duke"); System.out.println(duke == anotherDuke);
前の例に基づいて、このコードはtrue
を返すと思うかもしれませんが、そうではありません。 new
演算子を追加すると、メモリに新しいString
オブジェクトが作成されます。 したがって、JVMは2つの異なるオブジェクトを作成します。
ネイティブメソッド
Javaのネイティブメソッドは、通常、メモリの管理とパフォーマンスの最適化を目的として、C言語を使用してコンパイルされるメソッドです。
文字列プールとintern()
メソッド
プールに文字列を格納するには、文字列インターンと呼ばれるメソッドが使用されます。
Javadocがintern()
メソッドについて教えてくれるのは次のとおりです。
public native String intern();
intern()
メソッドは、文字列プールに文字列を格納するために使用されます。 最初に、既存の行がプールに存在するかどうかを確認します。 そうでない場合は、プールに新しい行を作成します。 行プールのロジックは、 Flyweightパターンに基づいています。
次に、 new
を使用して2行を作成するとどうなるかを確認します。
String duke = new String("duke"); String duke2 = new String("duke"); System.out.println(duke == duke2);
new
キーワードを使用した前の例とは異なり、この場合、比較はtrue
を返しtrue
。 これは、 intern()
メソッドを使用すると、文字列がプール内にあることが保証されるためです。
String
クラスのequals
メソッド
equals()
メソッドは、2つのクラスが同じかどうかを確認するために使用されます。 equals()
はObject
クラスに配置されるため、すべてのJavaクラスがそれを継承します。 ただし、 equals()
メソッドは、正しく機能するためにオーバーライドする必要があります。 もちろん、 String
equals()
オーバーライドします。
ご覧ください:
public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String aString = (String)anObject; if (coder() == aString.coder()) { return isLatin1() ? StringLatin1.equals(value, aString.value) : StringUTF16.equals(value, aString.value); } } return false; }
ご覧のとおり、 String
クラスの値は、オブジェクト参照ではなくequals()
を介して比較されます。 オブジェクトへの参照が異なっていても問題ありません。 条件が比較されます。
一般的なString
メソッド
文字列比較の問題を解決する前に知っておく必要があることがもう1つあります。
String
クラスの最も一般的なメソッドを検討してください。
文字列比較の問題を解決する
小さなパズルを解いて、 String
クラスについて学んだことを確認しましょう。
このタスクでは、学習した概念を使用して複数の行を比較します。 以下のコードを見て、各変数result
値を決定できますか?
public class ComparisonStringChallenge { public static void main(String... doYourBest) { String result = ""; result += " powerfulCode ".trim() == "powerfulCode" ? "0" : "1"; result += "flexibleCode" == "flexibleCode" ? "2" : "3"; result += new String("doYourBest") == new String("doYourBest") ? "4" : "5"; result += new String("noBugsProject") .equals("noBugsProject") ? "6" : "7"; result += new String("breakYourLimits").intern() == new String("breakYourLimits").intern() ? "8" : "9"; System.out.println(result); } }
結論はどうなりますか?
- A:02468
- B:12469
- C:12579
- D:12568
正解は記事の最後に記載されています。
今何が起こったの? String
動作を理解する
最初の行には以下が表示されます。
result += " powerfulCode ".trim() == "powerfulCode" ? "0" : "1";
この場合、 trim()
メソッドがスペースを削除すると、 new
演算子を使用してnew
String
が作成されるため、結果はfalse
です。
次に見るもの:
result += "flexibleCode" == "flexibleCode" ? "2" : "3";
ここには秘密はありません;行は行プールで同じです。 この比較はtrue
返しtrue
。
次に、次のものがあります。
result += new String("doYourBest") == new String("doYourBest") ? "4" : "5";
new
を使用すると、2つの新しい行が作成され、それらの値が等しいかどうかは関係ありません。 この場合、値が同じであっても比較はfalse
になりfalse
。
次:
result += new String("noBugsProject") .equals("noBugsProject") ? "6" : "7";
equals()
メソッドを使用したため、オブジェクトのインスタンスではなく、文字列値が比較されます。
この場合、値が比較されるため、異なるオブジェクトであるかどうかは関係ありません。 結果はtrue
です。
最後に、次のものがあります。
result += new String("breakYourLimits").intern() == new String("breakYourLimits").intern() ? "8" : "9";
前に見たように、 intern()
メソッドは文字列を文字列プールに入れます。 両方の行が同じオブジェクトを指しているため、この場合はtrue
です。
一般的な文字列エラー
特に線に同じ値が含まれている場合、2つの線が同じオブジェクトを指しているかどうかを判断するのは困難です。 文字列値が同じであっても、newを使用すると、常にメモリ内に新しいオブジェクトが作成されることに注意してください。
String
メソッドを使用してオブジェクト参照を比較することも難しい場合があります。 特異性は、メソッドが行の何かを変更すると、オブジェクトへの異なる参照が存在することです。
明確にするのに役立ついくつかの例:
System.out.println("duke".trim() == "duke".trim());
trim()
メソッドは新しい行を作成しないため、この比較は真になります。
System.out.println(" duke".trim() == "duke".trim());
この場合、最初のtrim()
メソッドは新しい行を生成します。メソッドがその仕事をするため、リンクが異なるためです。
最後に、 trim()
が処理を行うと、新しい行が作成されます。
文字列について覚えておくべきこと
行は変更できないため、行の状態は変更できません。
メモリを節約するために、JVMは文字列を文字列プールに保存します。 新しい行を作成するとき、JVMはその値をチェックし、既存のオブジェクトを指します。 プールにこの値の行がない場合、JVMは新しい行を作成します。
演算子「 ==
」はオブジェクト参照を比較します。 equals()
メソッドは文字列値を比較します。 同じルールがすべてのオブジェクトに適用されます。
new
演算子を使用すると、同じ値の行が存在する場合でも、ヒープに新しい行が作成されます(翻訳者の注意-元々はプールにあると書かれていますが、そうではありません)。
答え
この問題に対する答えはDです。結論は12568です。
続行するには...