優しい夜
第7章 オブジェクトとコレクション(SJC-P 1.4)
2007-05-12-Sat  CATEGORY: Java
≫目次
■試験に出るObjectクラスのメソッド
■toString()メソッド
■equals()メソッドのオーバーライド
■hashCode()メソッド
■コレクション
■ガーベジコレクション


Javaの世界では、基本型を除く、あらゆるものがオブジェクト。

■試験に出るObjectクラスのメソッド
public String toString()…オブジェクトの内容を文字列で返す
public boolean equals(Object obj)
public int hashCode()…オブジェクトのint型のハッシュコードを返す。
protected void finalize() throws Throwable
public final void notify()
public final void notifyAll()
public final void wait()

■toString()メソッド
System.out.println()メソッドにオブジェクトの参照を渡すと、そのオブジェクトのtoString()メソッドが自動的に呼び出される。
オーバーライドされていないtoString()メソッドが、デフォルトで返す文字列は、
「クラス名@オブジェクトのハッシュコードを符号なし16進数で表記したもの」。
オーバーライド例:
public String toString(){
	return "出力したいオブジェクトの状態";
}
■equals()メソッドのオーバーライド
Stringクラスとラッパークラスは、equals()メソッドをオーバーライドしている。よって、これらのクラスは、HashtableやHashMapのキーとして安全に使用可能。(Collectionインタフェースもequals()メソッドをオーバーライドしている。)
自分でクラスを定義する場合は、equals()メソッドが必要かどうか(オーバーライドすべきかどうか)を判断する必要がある。

□equals()メソッドをオーバーライドしていないクラス
そのクラスの参照変数は、まったく同じオブジェクトを参照している場合のみ同一とみなされる。(「equalsメソッド」が「==演算子」と同じ動作をする)。

そのクラスの参照変数を、HashSetやHashMapのキーとして使用することは出来ない。
「オブジェクトを登録した時にキーとして使用した参照変数」と、「検索の時にキーとして使用する参照変数」が、同一であると判定する手段がない。
これはハッシュを使用するコレクションの中で、オブジェクトの検索が出来ない事を意味する。

つまり、自分で定義したクラスのオブジェクトをハッシュテーブルのキー(あるいは同様のオブジェクト検索/取得ができる任意のデータ構造の要素)として使いたい場合は、そのクラスの2つの異なるインスタンスを同一のものと見なせるように、equals()メソッドをオーバーライドする必要がある。

□equals()メソッドを実装(オーバーライド)する
0、オーバーライドの規則に従う(アクセス権public、戻り値boolean型、引数Object型)
1、「比較対象のオブジェクトの型が正しいか」を確認するため、また「オブジェクト引数を正しい型にキャスト可能か」を確認するために、instanceofテストを行う。
※instanceofテストを省略した場合、正しい型にキャストできないオブジェクト引数に対して比較を行おうとすると、ランタイム例外ClassCastExceptionが発生する。
※equals()の引数がObject型の為、対象のメソッド(属性)にアクセスするために、キャストしなければならない。
※短絡&&演算子を使えば、instanceofテストがfalseの場合、型のキャストを行わないので、ランタイムエラーも発生しない。
2、対象のキーを比較する(どの属性をもって等しいとするかは設計者次第)
例:
public boolean equals(Object o){
	if((o instanceof Moof)&&(((Moof)o).getMoofValue())==this.moofValue){
		return true;
	}else{
		return false;
	}
}
equals()メソッドとhashCode()メソッドのどちらかをオーバーライドした場合は、もう一方もオーバーライドしなければならない。

□equals()メソッドの規則
反射性、対称性、推移性、整合性、「引数がnullの場合」を正しく実装する。

■hashCode()メソッド
ハッシュを使ってうまく検索するには、次の2つの手順を踏むのが一般的
1、目的のバケツを見つける。(hashCode()での一致)
2、そのバケツの中を調べて、目的の要素を見つける。(equals()での一致)

コレクションからオブジェクトを見つける為には、探したいオブジェクトのハッシュコードと、コレクション内に(保存されている)オブジェクトのハッシュコードが同じであり、かつ、両オブジェクトをequals()メソッドで判定したときにtrueとなる必要がある。

自作クラスのオブジェクトを、ハッシュを使うコレクション内で確実に使用できるようにする為には、equals()とhashCode()の両方をオーバーライドするしか方法はない。

□hashCode()メソッドを実装(オーバーライド)する
例:
class HasHash{
	public int x;
	HasHash(int xVal){ //コンストラクタ
		x=xVal;
	}
	public boolean equals(Object o){
		//キャストの前に必ずinstaenceofテストをする
		if(o instanceof HasHash){
			HasHash h = (HasHash)o;
			
			if(h.x == this.x){
				return true;
			}else{
				return false;
			}	
		}
	}
	public int hashCode(){
		return (x*17);
	}
}
上記の例では、2つのオブジェクトの変数xが同じ値であれば、equals()メソッドはその2つのオブジェクトを等しいと判定します。
したがって、同じx値を持つオブジェクトは同じハッシュコードを返すように実装しなければなりません。

□hashCode()メソッドの規則
2つのオブジェクトがequals()メソッドで等しい時、両者のハッシュコード値は同じ整数値でなければならない。

2つのオブジェクトがequals()メソッドで等しくない時、両者のハッシュコード値は異なる整数値でなくてもかまわない。しかし、異なる整数値が生成されるようにすれば、ハッシュテーブルのパフォーマンスが上がる。

等しいかどうかに関係なく、全てのインスタンスについて同じ整数値を返すhashCode()メソッドは、極めて非効率的であるが、メソッドとして完全に有効。規則には反していないので適切ですらある。

(条件) (必須) (任意)
x.equals(y) == true x.hashCode() == y.hashCode()  
x.hashCode() != y.hashCode() x.equals(y) == false  
x.hashCode() == y.hashCode()   x.equals(y) == true
x.equals(y) == false   要件なし

hashCode()メソッドでは、インスタンス変数を^(排他的論理和)演算し、その結果に素数を掛け合わせるのが一般的。

一時変数transientは保存されないので、オブジェクトのハッシュコードの計算や等価比較に使用するのは適切ではない。

hashCode()の
「有効」なオーバーライド…コンパイルして実行可能なもの
「適切」なオーバーライド…規則に従っているもの
「効率的」なオーバーライド…バケツ全体にキーをランダムに配布

□適切なhashCode
アプリケーション実行中で、オブジェクトが変更されていなければ、同じ整数を返す。
(アプリケーションを再実行したなら、新たな値が設定されても良い。)
equalsで等しいとされるものは、同じ値を返す。

■コレクション
どのコレクションクラスでも、サイズを拡大縮小することができる。
インタフェース6種、実装クラス10種
                                                     順序  ソート
(Collection)┬(List:インデックス) ┬─↓Vector        ○     ×
            |                   ├─ArrayList       ○     ×
            |                   └─LinkedList      ○     ×
            |
            └(Set:一意の物事)──┬─HashSet         ×     ×
                    |           └─LinkedHashSet   ○     ×
                    └(SortedSet) ──TreeSet         ○     ○

(Map:一意のIDを持つ物事)─────┬─↓Hashtable     ×     ×
            |                   ├─HashMap         ×     ×
            |                   └─LinkedHashMap   ○     ×
            └────(SortedMap) ──TreeMap         ○     ○
「Collection」は、コレクションでよく使われるメソッドが宣言されているインタフェース。
「Collections」は、コレクション関連の静的メソッドが定義されているクラス。
「Iterator」反復子。コレクションの要素の繰り返し処理を統一的に扱うインタフェース。
(iterateの意味⇒繰り返す; 繰り返して言う)
iterator()メソッド…Collectionで定義されている反復子を返すメソッド。

コレクションにおける反復処理とは、通常は、先頭の要素から各要素を順に見て行くこと。

順序…挿入/アクセス日付やインデックス位置によって決まる順序。

ソート(自然順序)…Stringオブジェクトのコレクションの場合は、アルファベット順。Integerオブジェクトのコレクションの場合は数値順。またはプログラマーが独自に定義。
「ソート済み」コレクションとは「自然順序でソートされているコレクション」のこと。TreeSet, TreeMap

≪List≫
要素の順序をインデックスに基づいて決定する。
要素のインデックス位置は、追加先の位置を指定した場合はその位置に置かれ、指定しなかった場合はリストの末尾に置かれる。
オブジェクトの重複可能。

□Vector
同期化されたArrayList。
主要なメソッドが同期化されている。
同期メソッドを使うとパフォーマンスが低下するので、ArrayListを使った方が良い。
RandomAccessインタフェースを実装している。

□ArrayList
反復処理とランダムアクセスを高速に行うことができる。
反復処理の速度を向上させる必要があり、なおかつ挿入や削除の操作があまり多くない時には、LinkedListよりもArrayListの方が適している。
RandomAccessインタフェースを実装している。
変数alをArrayListオブジェクトの参照変数とする時の、全ての要素の出力コード
例1)for(int i=0,i<al.size();i++) 
		System.out.println(al.get(i));

例2)for(int i=0,i<al.size();i++) 
		System.out.println(al.elementAt(i));

例3)Iterator it = al.iterator();
		while(it.hasNext())
			System.out.println(it.next());

例4)for(Iterator it = al.iterator();it.hasNext();)
			System.out.println(it.next());
□LinkedList
要素同士が双方向にリンクされている。
リストの先頭または末尾で要素を追加/削除するメソッドを持つ。
要素を末尾に追加したい場合(スタックやキューの実装など)に向いている。
ArrayListに比べ、反復処理の速度は落ちるが、挿入や削除を高速に行いたい時には便利。

≪Set≫
オブジェクトの重複不可。
等しいかどうかは、equals()メソッドによって判定される。等しいと判定された場合は、一方のオブジェクトだけがセットに格納される。

□HashSet
オブジェクトの挿入時にハッシュを使う。
重複を認めず、かつコレクションの反復処理の順序を問わない時に使用する。

□LinkedHashSet(ver1.4から)
HashSetを順序付きにして、要素間の双方向リンクを利用できるようにしたもの。
決まった順序で反復処理を行いたい時には、HashSetではなくLinkedHashSetを使う。
要素を挿入順に反復処理する代わりに、「アクセス日付」の新しいものから順に処理することも可能。

□TreeSet
各要素はそのクラスに定義されている自然順序に従って昇順に並べられる。
使用例:
Collection c = new TreeSet();
c.add("hello");
c.add("byebye");
c.add("hello");
Iterator it = c.iterator();
while(it.hasNext())
	System.out.println(it.next());
≪Map≫
「キー」を使用。
「キー」の重複不可。
「キー」と「値」はどちらもオブジェクト。
キーがマップのどの場所に格納されるのかはキーのハッシュコードに左右される。

□Hashtable
同期化されたHashMap。
主要なメソッドが同期化されている。
Hashtableではキーにも値にもnullが認められていない。
使用例:
Hashtable h = new Hashtable();
h.put("1","John");
h.put("2","Mary");
h.put("3","Tom");
String s = (String)h.get("3"); //getはObjectを返すので、
System.out.println(s);         //キャストしないとコンパイルエラー
□HashMap
マップが必要だが反復処理の順序は問わないという場合に使う。
マップの中で更新処理の速度が一番速い。
コレクション内にnullキーを1つだけ持つことができ、null値はいくつあってもかまわない。

□LinkedHashMap(ver1.4から)
HashMapを順序付きにして、要素間の双方向リンクを利用できるようにしたもの。
決まった順序で反復処理を行いたい時には、HashMapではなくLinkedHashMapを使う。
要素を挿入順に反復処理する代わりに、「アクセス日付」の新しいものから順に処理することも可能。
キャッシュに適したコレクション。FIFO(先入れ先出し)でオブジェクトを取り出すことができる。キャッシュに格納した順にLinkedHashMapの要素を取り出すには、values()メソッドでコレクションを取得し、そのコレクションを反復処理する。
要素の追加や削除の速度はHashMapよりもいくぶん遅いが、反復処理の速度は速い。更新処理の速度が速い。
nullキーは1つ、null値はいくつでも。

□TreeMap
自然順序でソート済みのMap。

■ガーベジコレクション
プログラム実行時にメモリーを消費するもの…スタック、ヒープ、定数プール、メソッド領域。
上記の中でガーベジコレクションの処理に関係するのはヒープだけ。
全てのオブジェクトはヒープ上に置かれる。

実行中のJavaプログラムから「到達不能」になったオブジェクトはガーベジコレクションの対象になる。
あるオブジェクトにアクセスしている生存中のスレッドが1つもない時、そのオブジェクトはガーベジコレクションの対象になる。
対象になるだけで削除がいつ実行されかの保証はない。

保障されているのはただ1つ、メモリーの空き容量が少なくなってきたら、OutOfMemoryExceprionが投げられる前に、ガーベジコレクタが実行されるということだけ。

オブジェクトを参照する参照変数があって、且つ生存中のスレッドがその変数を利用できれば、そのオブジェクトは「到達可能」と見なされる。
静的メンバーは、生存中のオブジェクトと見なされる。

ガーベジコレクタが動作すると、使われていないオブジェクトが全て削除されるまで、プログラムの実行が停止する。

ガーベジコレクションのアルゴリズムの詳細はわからない。

Javaプログラムがメモリーを使い果たすこともあり得る。

□オブジェクトをガーベジコレクションの対象にするには、
・参照をnullにする。
・参照変数の参照先を切り替える。
・参照を孤立させる(島)…互いに参照しあっているオブジェクトに対する他の参照が削除されると、生存中のスレッドはどちらのオブジェクトにもアクセスできなくなる。

□ガーベジコレクションの呼び出し
Runtime.getRuntime().gc();
System.gc();
※呼ぶ(要求する)だけで、実行される保証はない。

全メモリーの取得:Runtime.getRuntime().totalMemory();
空メモリーの取得:Runtime.getRuntime().freeMemory();

□finalize()メソッド
Objectクラスから継承するfinalize()メソッド内に、ガーベジコレクタが実行される直前に行いたいコードを記述する事が出来る。
ただし、ガーベジコレクションが必ず実行される保証はないので、finalize()メソッドも必ず実行されるという保証はない。

finalize()メソッドの呼び出しによって、オブジェクトが削除を免れることがある(finalize()メソッドの中で、ガーベジコレクションの対象になっているオブジェクトの参照を別のオブジェクトに渡せば)。

どんなオブジェクトでも、finalize()メソッドがガーベジコレクタから呼び出されるのは一度だけ(上記の方法でオブジェクトが削除されのを免れるのは一度のみ)。

Trackbacks0 Comments0
Comments

Only the blog author may view the comment.
 
Trackbacks
TB*URL
ページトップへ
Copyright © 2009 優しい夜. all rights reserved.