【Java】 String型の正しい比較


String型比較を深堀してみる

String型の値の比較について、整理しました。

等比較の方法

1. ==

 ・プリミティブ型では値の比較を行う。
 ・オブジェクト型ではオブジェクトの参照値を比較し、オブジェクトが同一か判断する。

2. equals()

 オブジェクト型でのみ使用可能で、オブジェクトの内容比較を行い、オブジェクトが同値か判断する。
 ※Objectクラスにequalsが定義されている。
  Stringクラスではoverrideしている。

つまり、Strng型は参照型のオブジェクトだから、equalsを使えばOKですね。

検証(==だと本当に比較できないのか)

StringSample.java
    String str1 = "A";
    String str2 = "A";
    String str3 = new String("A");
    System.out.println(str1.hashCode());
    System.out.println(str2.hashCode());
    if(str1 == str2) {
        System.out.println("TRUE");
    }else {
        System.out.println("FALSE");
    }
    if(str1 == str3) {
        System.out.println("TRUE");
    }else {
        System.out.println("FALSE");
    }

結果

100
100
TRUE

TRUEになった。String型は==でも比較していいのか。

調査

String型は初期化された際にヒープ領域に値が作られるが、同じ値を使用する場合は使いまわすらしい。

※newされた場合は新しく領域が作られる。

図解してみた

メモリ節約のため、newせずに値"A"が一致する領域(アドレス=100)を使いまわす。
※参照値:参照先のメモリアドレス

Stringの値を変更した場合の動作

String比較2.png

変数の値を書き換えると参照元の値(アドレス=100)が変更されるのではなく、"B"の新しい領域をnewして参照先を変更するんですね。(参照先アドレスを100→200に変更)
※Stringはイミュータブルのため、参照先の値を変更できない。

Stringオブジェクトのequalsはなにをやっているか

String.class
    private final char value[];

    public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;                //自オブジェクトのvalue
                char v2[] = anotherString.value;  //比較先オブジェクトのvalue
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])           //Stirngをcharに分解して値比較
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

Stringオブジェクトをchar[]に分解して1つずつ値を比較している。

結論

Stringオブジェクトはメモリ節約のために同じ値の場合は異なるローカル変数でも参照値(参照先アドレス)が同一になる可能性があるが、保証はされていないので、equalsで比較するべし。