web-dev-qa-db-ja.com

"" == sが偽であるが、 "" .equals(s)が真の場合

[〜#〜] edit [〜#〜]プロンプト応答をありがとう。本当の質問が何であるかを見てください。今回は大胆にしました。

==と.equalsの違いは理解しています。だから、それは私の質問ではありません(私は実際にそのためのいくつかのコンテキストを追加しました)


空の文字列に対して以下の検証を実行しています。

if( "" == value ) { 
    // is empty string 
} 

過去で、dbから値をフェッチするとき、または別のノードからオブジェクトを逆シリアル化するとき、このテスト失敗 2つの文字列インスタンスは実際には異なるオブジェクト参照でしたが、同じものが含まれていましたデータ。

したがって、これらの状況の修正は

if( "".equals( value ) ) {
   // which returns true for all the empty strings
}

私はそれで大丈夫です。それははっきりと理解されています。

今日、これはもう一度起こりましたが、今回はアプリケーションが非常に小さいため、私は戸惑いましたスタンドアロンアプリケーション使用しないネットワーク、新しい文字列はフェッチされませんデータベースから、または別のノードから非セリゼーションされます。

したがって、問題は次のとおりです。


その下で[〜#〜] other [〜#〜]状況:

"" == value // yields false 

そして

"".equals( value ) // yields true

ローカルスタンドアロンアプリケーションの場合?

私はかなり確信していますnew String()はコードで使用されていません。

そして、文字列参照が ""になる唯一の方法は、次のようにコード内で直接 ""が割り当てられているためです(またはそれが私が考えたものです)。

String a = "";
String b = a;

assert "" == b ; // this is true 

どういうわけか(コードをもっと読んだ後、私は手がかりがあります)2つの異なる空の文字列オブジェクト参照が作成されました、私は知りたいですhow

Jjnguysの回答の行の詳細:

バイト!

編集:結論

私はその理由を見つけました。

Jjnguyの提案の後、私はコードを別の目で見ることができました。

有罪のメソッド:StringBuilder.toString()

新しいStringオブジェクトが割り当てられ、初期化されて、このオブジェクトによって現在表されている文字シーケンスが含まれます。

ドー!.。

    StringBuilder b = new StringBuilder("h");
    b.deleteCharAt( 0 );
    System.out.println( "" == b.toString() ); // prints false

謎が解けた。

このコードは、StringBuilderを使用して、増え続ける文字列を処理します。ある時点で誰かがしたことが判明しました:

 public void someAction( String string ) { 
      if( "" == string ) {
           return;
       }

       deleteBankAccount( string );
 }

と使用

 someAction( myBuilder.toString() ); // bug introduced. 

p.s.最近、CodingHorrorを読みすぎましたか?または、なぜここに面白い動物の写真を追加する必要があると感じるのですか?

33
OscarRyz
_String s = "";
String s2 = someUserInputVariale.toLowercase(); // where the user entered in ""
_

そのようなものは、_s == s2_がfalseと評価される原因になります。

多くのコードは、呼び出しをnew String()に公開せずに、新しいStringsを作成します。

26
jjnguy
"" == value // yields false

そして

"".equals( value ) // yields true

変数valueの値がインターンされていないときはいつでも。これは、値が実行時に計算される場合に当てはまります。 JLSセクション3.10.5文字列リテラル を参照してください。これを示すコードの例:

したがって、コンパイルユニット(§7.3)で構成されるテストプログラム:

package testPackage;
class Test {
    public static void main(String[] args) {
        String hello = "Hello", lo = "lo";
        System.out.print((hello == "Hello") + " ");
        System.out.print((Other.hello == hello) + " ");
        System.out.print((other.Other.hello == hello) + " ");
        System.out.print((hello == ("Hel"+"lo")) + " ");
        System.out.print((hello == ("Hel"+lo)) + " ");
        System.out.println(hello == ("Hel"+lo).intern());
    }
}
class Other { static String hello = "Hello"; }

およびコンパイルユニット:

package other;
public class Other { static String hello = "Hello"; }

出力を生成します:

true true true true false true

この例は、6つのポイントを示しています。

  • 同じパッケージ(§7)内の同じクラス(§8)内のリテラル文字列は、同じStringオブジェクト(§4.3.1)への参照を表します。
  • 同じパッケージ内の異なるクラス内のリテラル文字列は、同じStringオブジェクトへの参照を表します。
  • 同様に、異なるパッケージの異なるクラス内のリテラル文字列は、同じStringオブジェクトへの参照を表します。
  • 定数式(§15.28)によって計算された文字列は、コンパイル時に計算され、リテラルであるかのように扱われます。
  • 実行時に計算される文字列は新しく作成されるため、区別されます。
  • 計算された文字列を明示的にインターンした結果は、同じ内容の既存のリテラル文字列と同じ文字列になります。
21
Bill the Lizard

ジョシュア・ブロックとニール・ガフターの本Javaパズルを手に入れて、パズル13「動物農場」を見てください...彼この問題について素晴らしいアドバイスがあります。関連するテキストをいくつかコピーします。

「タイプStringのコンパイル時定数がinterned[JLS 15.28]であることをご存知かもしれません。言い換えると、任意の2つ同じ文字シーケンスを指定するタイプStringの定数式は、同一のオブジェクト参照によって表されます... コードはめったにありませんが、ある場合は、文字列定数のインターンに依存します。インターンは、プログラマー向けのツールとしてではなく、仮想マシンのメモリフットプリントを削減するためだけに設計されました...オブジェクト参照を比較するときは、equalsメソッドよりも== operator値ではなくオブジェクトIDを比較する必要がない限り。 "

それは私が言及した上記の参考文献からです...私の本の30〜31ページ。

10
Tom

"abcde".substring(1,2)"zbcdefgh".substring(1,2)が同じStringオブジェクトを生成すると思いますか?

これらは両方とも、2つの異なる文字列から抽出された「等しい」サブ文字列を生成しますが、tehyが異なるオブジェクトであることは非常に合理的であると思われるため、==はそれらを異なるものと見なします。

ここで、部分文字列の長さが0の場合、substring(1, 1)について考えます。長さゼロの文字列が生成されますが、"abcde".substring(1,1)"zbcdefgh".substring(1,2)とは異なるオブジェクトであるため、少なくとも1つが ""とは異なるオブジェクトであることは驚くことではありません。

7
djna

私が理解しているように、Javaコードをバイトコードにコンパイルするとき、またはプログラムを実行するとき、メモリを節約するために、ほとんどの場合、同じ文字列が同じオブジェクトを参照します。したがって、==で逃げることがあります。文字列の比較ですが、これは信頼できないコンパイラの最適化です。

しかし、コンパイラがこの最適化を行わないことを決定したり、プログラムが文字列が同じであることを確認する方法がなく、依存するいくつかの基礎となる最適化ブードゥーに依存しているため、突然チェックが失敗することがあります。使用しているjvmの実装など。

したがって、equalsを使用することは常に良いことです。空の文字列の場合、length == 0と比較するなど、他の可能性があります。下位互換性を気にしない場合は、string.empty()があります。

3
Janusz

String.length() == 0を検討してみてください。

2
Paul Sonier

使用しない理由:

_if (value != null && value.length == 0) {
    // do stuff (above could be "== null ||"
}
_

オブジェクトの_==_は参照を比較するため、つまり同じオブジェクトであるため、equals()を使用する必要があります。コンパイル時にJavaは同一の文字列を検索し、それらが同じ参照を共有するようにします(文字列は不変です)が、実行時には、異なる参照を持つ空の文字列を簡単に作成できます。 equals()の典型的な意図。

0
JeeBee

このリファレンスを確認してください: http://mindprod.com/jgloss/string.html#COMPARISON 優れた カナダのマインド製品Java&インターネット用語集 。ブックマークする価値があります。

0
artlung

javadoc for String.intern()には、_==_と.equals()についての良い解説があります。

ドキュメントはまた、すべての文字列リテラルがintern 'dであることを明確にしています。

public String intern()

文字列オブジェクトの正規表現を返します。

最初は空の文字列のプールは、Stringクラスによってプライベートに維持されます。

インターンメソッドが呼び出されたときに、equals(Object)メソッドによって決定されたこのStringオブジェクトに等しい文字列がプールにすでに含まれている場合、プールからの文字列が返されます。それ以外の場合、このStringオブジェクトはプールに追加され、このStringオブジェクトへの参照が返されます。

したがって、任意の2つの文字列sおよびtについて、s.equals(t)が真である場合に限り、s.intern()== t.intern()が真になります。

すべてのリテラル文字列と文字列値の定数式がインターンされます。文字列リテラルは、Java言語仕様の§3.10.5で定義されています

戻り値:この文字列と同じ内容の文字列ですが、一意の文字列のプールからのものであることが保証されています。

0
Alex B

グーグルコード検索を使用すると、人々がこれと同じエラーを起こす場所をたくさん見つけることができます: google for file:.Java\=\=\\ "\" もちろん、これは正しい可能性があります注意深く制御された状況でのイディオムですが、通常、それは単なるバグです。

0
Trevor Harrison