Back To Main

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
┃ ◆Javaで珈琲ブレイク vol.012 前編◆
┃……………………………………………………………………………………………
┃ [不定期] まぐまぐ ID=0000088576 Melma! ID=m00061296
┃……………………………………………………………………………………………
┃ 今回からご覧になる方は、バックナンバーご活用下さい
┃ http://javacafebreak.tripod.com
┃ http://www.melma.com/mag/96/m00061296/
┃ http://backno.mag2.com/reader/Back?id=0000088576
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

◆目次◆

■ホームページ・掲示板変更のお知らせ
■プリミティブ型の比較(==、イーコル・イーコル)
■参照型の比較 (.equals(ドット・イーコル)と==(イーコル・イーコル))
■Stringクラスのequals()メソッド
■参照の一致と内容の一致


皆さんこんにちは。

Mr.Hackです。お元気ですか?

■ホームページ・掲示板変更のお知らせ
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

まずは、皆さんに掲示板とホームページの変更のお知らせです。今まで使用していたところがサーバーが重かったり、広告の窓が勝手に開く等、使い勝手が悪かったので、下記に変更することにしました。ブックマークをされている方は、恐れ入りますが、変更して頂けるようお願い致します。

ホームページ :http://javacafebreak.tripod.com
Java Cafe Room:https://javacafebreak.tripod.com/cafe_entrance.html

内容的には、変わっておりませんが、幾分アクセスパフォーマンスが良くなったのではないかと思います。引き続きどうぞ宜しくお願い致します。


さて、前置きはこのくらいにしておきまして、今日もがんばっていきましょう。前回は、コンソールベースのユーザーインターフェイス部分を改良してメインメニューを設計して、isContinued()メソッドを見てみました。その中で、条件式の比較に、char型ではなく、String型で比較をしました。そこで、僕は、==(イーコル・イーコル)を使うな、といいました。何故、String型の文字列比較で、==を使ってはならないのでしょう。これは、参照という概念と密に関係しています。String型の文字列比較の謎を解く前に、intやdoubleのようなプリミティブ型(Primitive Type)の比較をまず見てみましょう。


■プリミティブ型の比較(==、イーコル・イーコル)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Javaには、プリミティブ型としてはbyte, short, char、int、long、float、 double、booleanがあります(String型はプリミティブ型ではない)。これらの型の変数を比較する場合は、必ず、==を使います。例えば、

┌──────────────────────────────────┐

int a = 6;
int b = 6;
if (a == b) {
System.out.println("aとbは同じ値である");
}
...

└──────────────────────────────────┘

というように使います。boolean型も同じように、

┌──────────────────────────────────┐

boolean answer = false;

if (answer == false) {
System.out.println("answerはfalseである");
}
...
└──────────────────────────────────┘

とできますね。つまり、プリミティブ型は、==を使うことによって、a、b、answerに代入された値を、そのまま比較することが出来るのです。

それでは、クラスや配列である参照型(Reference Type)はどうでしょう?


■参照型の比較 .equals(ドット・イーコル)と==(イーコル・イーコル)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
実は、参照型の比較も==でいいのです。.equalsも全く同じ意味なのです。

┌──────────────────────────────────┐
え?.equalsは、==とちがって、内容の比較だと習ったけど?
└──────────────────────────────────┘

これは、正しくありません。正確には、

┌──────────────────────────────────┐
String型の.equals()は、==の比較に加えて、文字列の内容の比較もする
└──────────────────────────────────┘

が正しいです。

???ですか?では、それを裏付けて見ましょう。

まずは、Java SDK API仕様から見てみましょう。APIにいって、Objectクラスを開いて下さい(未だAPIの見方が分からない人は、vol.003を参照)。

Objectクラスを開きましたか?そこのトップにはなんと書いてありますか?

┌──────────────────────────────────┐

Object クラスは、クラス階層のルートです。すべてのクラスは、スーパー
クラスとして Object を持ちます。配列を含むすべてのオブジェクトは、
このクラスのメソッドを実装します。

└──────────────────────────────────┘

ここの『すべての』に注目です。Java SDK APIで提供されるクラスはもちろん、あなたが自分で作ったクラスも含め、『すべて』のクラスは、『Object』をスパークラスに持つのです。そして、このObjectクラスを継承する『すべての』クラスは、Objectクラスの『メソッド』を実装(持つ)のです。継承は、まだ習っていない概念ですが、ここでは簡単に、親であるクラスの性質(メソッド等)を、子であるクラスが引き継ぐこと、と覚えておいて下さい。

この『メソッド』には、何がありますか?API仕様をスクロールしてみましょう。メソッドの概要には、clone()メソッドから始まって、お、ありましたね、equals()メソッド。つまり、

┌──────────────────────────────────┐
equals()メソッドは、『すべての』クラスに実装される
└──────────────────────────────────┘

訳ですね。僕が作ったクラスであろうと、あなたが作ったクラスであろうと、Stringクラスであろうと、『すべて』のクラスが、equals()メソッドを実装します。配列のオブジェクトもまたしかりです。それで、equals()メソッドのリンクをクリックしてみますと、

┌──────────────────────────────────┐

Object クラスの equals メソッドは、もっとも比較しやすいオブジェクト
の同値関係を実装します。つまり、す べての参照値 x と y について、こ
のメソッドは x と y が同じオブジェクトを参照する (x==y が true) 場合
にだけ true を返します。

└──────────────────────────────────┘

『同じオブジェクトを参照する(x==yがtrue)場合だけtrueを返す』とありますね。つまり、.equals()メソッドは、==と全く同じなのです。

え?これでも納得いきませんか?それでは、Objectクラスのソースコードを見てみましょう。見方は、『vol.004 vol.005番外編』を参照して下さい。Objectクラスは、java\langフォルダ以下にあります。

開きましたか?Objectクラスのequals()メソッドを見ると、

┌──────────────────────────────────┐

public boolean equals(Object obj) {
return (this == obj);
}

└──────────────────────────────────┘

たった、これだけです。this.はObjectクラスのオブジェクト自身をさしますから、まさに、==で比較していますね。つまり、Objectクラスを継承する『全ての』クラスは、『特別に』Override(上書き。継承しているサブクラスで新たにequals()メソッドを定義)しない限り、.eqauls()は==と同じ意味なのです。


■Stringクラスのequals()メソッド
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

それでは、Stringクラスの.equals()も同じですか?感の良い方は気づかれましたね。そう、Stringクラスは、『特別に』equals()メソッドをOverrideしているために、

┌──────────────────────────────────┐
String型の.equals()は、==の比較に加えて、文字列の内容の比較もする
└──────────────────────────────────┘

のです。実際に、Stringクラスのソースコードを先ほどと同じように、src.jarから見てみましょう。StringクラスもJava\langフォルダにあります。そのStringクラスのequals()メソッドを見ると、

┌──────────────────────────────────┐

public boolean equals(Object anObject) {
// ここでthisとanObjectが同じオブジェクトを参照しているかチェック
// 同じメモリー領域を参照していれば、同じ内容なので、trueを返す
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = count;
// thisとanotherObjectの文字列の長さが同じ場合
if (n == anotherString.count) {
char v1[] = value;
char v2[] = anotherString.value;
int i = offset;
int j = anotherString.offset;
// 一文字一文字thisとanotherStringオブジェクトを比較し、一文字でも // 違えば、falseを返す。
while (n-- != 0) {
if (v1[i++] != v2[j++]) {
return false;
}
}
// 全ての文字が同じとき、trueを返す。
return true;
}
}
// anObjectがString型のオブジェクトではない場合は、もちろんfalse
return false;
}

└──────────────────────────────────┘

まず、==で比較していますね。==は参照先が同じという意味ですから、その場合は、無条件で、Stringの文字列も全て同じになりますので、trueを返します。その後は、一文字一文字、char型を比較して、一字でも違うと、whileループのなかでfalseを返します。ここが、文字列の内容の比較ですね。そして、全て同じ内容なら、whileループをぬけて、trueを返します。つまり、比較したStringオブジェクトは同じ内容ということになりますね。


■参照の一致と内容の一致
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

ここで、一致の比較には、2種類あることに注意して下さい。一つは、==とObjectクラスを継承しているクラスのequals()メソッドの、参照の一致(Equality of Reference)です。参照の一致とは、オブジェクトの参照変数が同じオブジェクト(同じメモリーアドレス)を参照していることをいいます。それに対して、Stringクラスのequals()メソッドのように内容までの一致を求めるものを、内容の一致(Equality of Content、またはオブジェクトの一致、Equality of Object)といいます。

その違いを、実際にメモリーアドレスの図で見てみましょう。

例えば、String型の参照変数、myStringとanotherStringを初期化すると

String myString = new String("Mr.Hack");
String anotherString = new String("Mr.Hack");

となりますね。これは、

String myString; ---- 1
myString = new String("Mr.Hack"); ---- 2
String anotherString = new String("Mr.Hack");

と同値であると、vol.003で述べました(詳しくはvol.003を参照)。1はStringクラスの参照変数の宣言(declaration)ですね。これは、Stack領域というメモリーに、32bits(4bytes)分の領域を確保して作成されます。この宣言は、あくまで参照変数で、2でStringのインスタンスを参照するまで、いわゆるゴミ値を保持します。

たとえば、myStringの宣言で、最初どこも参照していません。

┏━━ メモリー領域 ━━━━━━━━━━━━━━━━━━━━━━
┃ myString 0001023
┃ ┏━━━━┓ ┏━━━━━━━━━━━━━┓
┃ ┃ ごみ値
┃ ┗━━━━┛ ┗━━━━━━━━━━━━━┛


┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━


そして、2でnewキーワードを使っての初期化で、実際のオブジェクト分の領域(例えばスタートアドレスが、0001023)が、Heap領域というメモリーに確保されます。

┏━━ メモリー領域 ━━━━━━━━━━━━━━━━━━━━━━
┃ myString 0001023
┃ ┏━━━━┓ ┏━━━━━━━━━━━━━┓
┃ ┃ 0001023 --------------> Stringオブジェクトの領域
┃ ┗━━━━┛ ┗━━━━━━━━━━━━━┛


┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

ここで、参照の一致は、参照先がのオブジェクト(アドレス領域)が同じなので、以下のような、状況になります。参照型では、==とequals()メソッドがそうですね。


┏━━ メモリー領域 ━━━━━━━━━━━━━━━━━━━━━━
┃ myString 0001023
┃ ┏━━━━┓ ┏━━━━━━━━━━━━━┓
┃ ┃ 0001023 --------------> 'M''r''.''H''a''c''k'
┃ ┗━━━━┛ ----> ┗━━━━━━━━━━━━━┛
┃ /
┃ anotherString /
┃ ┏━━━━┓ /
┃ ┃ 0001023 ------/
┃ ┗━━━━┛

┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

myStringとanotherStringが同じところを参照していれば、その参照先のオブジェクトは一つですから内容も必ず同じになりますね。

一方で、内容の一致は、たとえ参照先が一致していなくても(参照の一致)、内容が一致していれば、一致とすることです。String#equals()メソッドでは、==でオブジェクトが同じ参照先かをチェックした後(同じ参照先でない場合)、今度はそれぞれのStringオブジェクトの内容である文字列を、一文字一文字チェックします。

┏━━ メモリー領域 ━━━━━━━━━━━━━━━━━━━━━━
┃ myString 0001023
┃ ┏━━━━┓ ┏━━━━━━━━━━━━━┓
┃ ┃ 0001023 --------------> 'M''r''.''H''a''c''k'
┃ ┗━━━━┛ ┗━━━━━━━━━━━━━┛

┃ anotherString 0002305
┃ ┏━━━━┓ ┏━━━━━━━━━━━━━┓
┃ ┃ 0002305 --------------> 'M''r''.''H''a''c''k'
┃ ┗━━━━┛ ┗━━━━━━━━━━━━━┛

┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

この場合、同じオブジェクトを参照していませんから、==ではfalseになりますが、myStringとanotherStringの一文字一文字を比較して、全て同じなら、内容の一致でtrueを返すというロジックになっています。

これが、String型では、==を使わずに、equals()メソッドを使うカラクリです。メモリー領域の話も一緒に理解することにより、==とequalsが本質的にどう違うかが分かると思います。Javaではメモリー管理はプログラマが出来ないので見えにくい部分ですが、がんばってこの概念を理解してみて下さい。もっとしっかり学んでみたいという方は、是非、C/C++を勉強指定見ることをお勧めします。

さて、equals()メソッドを要約しますと、

┌──────────────────────────────────┐

equals()は、==と同じ意味である。String#equals()は、==の比較に加え、
文字列の内容の比較もする

└──────────────────────────────────┘


ということになりますね。では、これを理解したところで、簡単な質問です。

┌──────────────────────────────────┐

int[] intArray1 = new Int[] {1, 2, 3};
int[] intArray2 = new int[] {1, 2, 3};

boolean answer = intArray1.equals(intArray2);

└──────────────────────────────────┘


のanswerはtrueでしょうかfalseでしょうか?String型の参照変数の代わりに、配列の参照変数です。もう皆さんはわかりますね(解答は、後編の一番下を参照)。

引き続き、後編をお楽しみ下さい。


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
◆Javaで珈琲ブレイク◆
───────────────────────────────────
皆様からの激励・批判のメールをお待ちしております。
【発行者】 Mr.Hack - javacafebreak@hotmail.com
【掲示板】 https://javacafebreak.tripod.com/cafe_entrance.html
※質問は上記の掲示板からどうぞ。
【サイト】 http://javacafebreak.tripod.com
【発行数】 まぐまぐ[943] Melma[143]
【解除】http://javacafebreak.tripod.com
※解除は上記のサイトからいつでもできます。
────────────────────────────────────
(c)2002 MR.HACK ALL RIGHTS RESERVED
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

© 2002 MR.HACK ALL RIGHTS RESERVED