(1) オープンアドレス法をJavaで実装したプログラムのサンプルをご紹介
(1-1) オープンアドレス法とは
(1-2) Javaのサンプルプログラムの全体像
(1-3) Javaのサンプルプログラム
(1-4) 参考(オープンアドレス法を適用する前のプログラム)
(1) オープンアドレス法をJavaで実装したプログラムのサンプルをご紹介
当記事では「オープンアドレス法」をJavaで実装した例をご紹介していきます。
(1-1) オープンアドレス法とは
(1-2) Javaのサンプルプログラムの全体像
まずはプログラムの内容を素早く把握するために、どのような構造になっているか?をざっくりご紹介します。
class CustomHashMap4 { //【メンバ変数】 public Word[] wordmix; int mapsize; //【コンストラクタ】CustomHashMapのコンストラクタ public CustomHashMap4(int mapsize) { this.wordmix = new Word[mapsize]; this.mapsize = mapsize; } //【値の更新】Wordクラスのインスタンスを更新 public void put(String w, int c) { //(インデックス計算+オープンアドレス法によるアドレス探索) //putの処理(アドレスの要素を更新) } //【値の取得】キーからインデックスを計算して値を取得 public int get(String w) { //(インデックス計算+オープンアドレス法によるアドレス探索) //getの処理(アドレスの要素を取得) } //【クラス】Wordクラス class Word { private String word; //Key private int wordcode; //Value public Word(String w, int c) {word = w; wordcode = c;} public String getKey() {return word;} public int getValue() {return wordcode;} public void setValue(int c) {wordcode = c; } } }
(構造の説明)
main文 | main文で実際にWordクラスのインスタンスを配列に追加(put)したり、値を取得(get)したりします。
この例で注目すべきが、単語「rumble」と「fairly」は共にハッシュが「369」となり「衝突」が起きています。同様に「severe」と「stuff」も共にハッシュが「372」で衝突が起きています。 |
【Wordクラス】 | 今回の配列の各要素に格納されるクラスです。 単語(word)と単語コード(wordcode)を保持しています。 今回の場合、main文にて配列の容量を1000確保しているので、配列にはWordクラスが最大1000個格納できます。 CustomHashMap4 cm = new CustomHashMap4(1000); |
【メンバ変数】 (wordmix、mapsize) |
Wordクラス型の配列wordmixと、そのサイズを格納するint型のmapsizeを定義しています。 |
【コンストラクタ】 (CustomHashMap4) |
int型のmapsizeを受け取り、配列wordmixを初期化します。 |
【値の取得】 (getメソッド) |
配列の要素を取得するためのgetメソッドを実装しています。
引数としてキー(w)と値(c)を受取り、値(c)のhashCodeから、格納先のインデックス(index)を計算して、もしそこが「空きアドレス」の場合は0を返し、「空きアドレスでない」場合は「既存の要素」が既にあるとみなし、格納されている値(wordcode)を取得します。 【処理フロー概要】 |
【値の更新】 (putメソッド) |
配列に要素を追加するためのputメソッドを実装しています。
引数としてキー(w)と値(c)を受取り、値(c)のhashCodeから、格納先のインデックス(index)を計算して、もしそこが「空きアドレス」の場合は新規のWordクラスのインスタンスを追加し、「空きアドレスでない」場合は「既存の要素」が既にあるとみなし、値(wordcode)の更新のみしています(同じキーの値だけを更新する)。 【処理フロー概要】 |
(1-3) Javaのサンプルプログラム
main文で実際にWordクラス型の配列wordmixに対してインスタンスを配列に追加(put)したり、値を取得(get)したりします。
この例で注目すべきが、単語「rumble」と「fairly」は共にハッシュが「369」となり「衝突」が起きています。同様に「severe」と「stuff」も共にハッシュが「372」で衝突が起きています。
しかし、オープンアドレス法が適用され、それぞれ+1したアドレスに無事に格納されています。
(サンプルプログラム)
public class IT0XXX_OpenAddress_After { public static void main(String args[]) { CustomHashMap4 cm = new CustomHashMap4(1000); cm.put("rumble", 15); //index=369に格納される cm.put("fairly", 20); //index=369でrumbleと衝突⇒370に格納される cm.put("severe", 10); //index=372 cm.put("stuff", 35); //index=372でsevereと衝突⇒373に格納される System.out.println("Point of team rumble : "+cm.get("rumble")); System.out.println("Point of team fairly : "+cm.get("fairly")); System.out.println("Point of team severe : "+cm.get("severe")); System.out.println("Point of team stuff : "+cm.get("stuff")); } } class CustomHashMap4 { //【メンバ変数】 public Word[] wordmix; int mapsize; //【コンストラクタ】CustomHashMapのコンストラクタ public CustomHashMap4(int mapsize) { this.wordmix = new Word[mapsize]; this.mapsize = mapsize; } //【値の更新】Wordクラスのインスタンスを更新 public void put(String w, int c) { //(1) インデックスの計算 int index = (w.hashCode() & 0x7fffffff) % mapsize; //#################### オープンアドレス方START #################### //(2) オープンアドレス法 // (2-1) 計算したアドレスが「衝突」しているか?をチェック // ①要素に値がある(使用されている) // ②要素に値がある場合、キーの値は異なるか? // →①と②両方に該当する場合にオープンアドレス法を実施 if(wordmix[index]!=null && !w.equals(wordmix[index].getKey())) { //格納アドレス探索完了フラグ boolean flg_canupdate = false; //(2-1-1)格納できるアドレスが見つかるまで繰り返し処理 // (格納アドレス探索完了フラグ=trueになるまで) // ⇒空きアドレスが見つかる or 既存の値と一致する=既出の場合にtrue while(!flg_canupdate) { //(2-1-1-1)インデックスをインクリメント index++; // (2-1-1-2)再計算したアドレスが衝突していないか?をチェック // ①要素に値がない(未使用)or ②キー値が同じか? if(wordmix[index]==null) { //(2-1-1-2-1)要素に値がない場合 // →未使用の領域である(空きアドレス) // →flag=trueに更新 flg_canupdate = true; } else if(wordmix[index]!=null && w.equals(wordmix[index].getKey())){ //(2-1-1-2-2)要素に値がありキー値が同じ場合 // →同じキー=衝突してないので、これ以上のindex加算は不要 // →flag=trueに更新 flg_canupdate = true; } //(2-1-1-2-3)空いてなくて既存キーしていない // →別のキーが既にいる(衝突)ので、次のアドレスに移動 // →何もせず(index++に戻り次のアドレスをチェック) } } //#################### オープンアドレス方END #################### //(3)値の追加処理 //(3-1)インデックスの要素がnullの場合 // →未使用の要素のため、新規Wordクラスのインスタンスを代入 if(wordmix[index]==null) { wordmix[index] = new Word(w,c); } //(3-2)インデックスの要素がnullでない場合 →既存更新 // →使用中の既存要素のため、既存のキーの値のみをsetValueで更新 else { wordmix[index].setValue(c); } } //【値の取得】キーからインデックスを計算して値を取得 public int get(String w) { //(1) インデックスの計算 int index = (w.hashCode() & 0x7fffffff) % mapsize; //#################### オープンアドレス方START #################### //(2) オープンアドレス法 // (2-1) 計算したアドレスが「衝突」しているか?をチェック // ①要素に値がある(使用されている) // ②要素に値がある場合、キーの値は異なるか? // →①と②両方に該当する場合にオープンアドレス法を実施 if(wordmix[index]!=null && !w.equals(wordmix[index].getKey())) { //格納アドレス探索完了フラグ boolean flg_canupdate = false; //(2-1-1)格納できるアドレスが見つかるまで繰り返し処理 // (格納アドレス探索完了フラグ=trueになるまで) // ⇒空きアドレスが見つかる or 既存の値と一致する=既出の場合にtrue while(!flg_canupdate) { // (2-1-1-1)インデックスを+1 index++; // (2-1-1-2)再計算したアドレスが衝突していないか?をチェック if(wordmix[index]==null) { //(2-1-1-2-1)要素に値がない場合 // →未使用の領域である(空きアドレス) // →flag=trueに更新 flg_canupdate = true; } // (2-1-1-2-2)空いてなくて既存キーの場合 // →同じキーなので、これ以上のindex加算は不要(衝突ではないので、このアドレスでOK) // →flag=trueに更新+値の返却処理で現在の値(Value)を返却 else if(wordmix[index]!=null && w.equals(wordmix[index].getKey())){ //(2-1-1-2-2)要素に値がありキー値が同じ場合 // →同じキー=衝突してないので、これ以上のindex加算は不要 // →flag=trueに更新 flg_canupdate = true; } // (2-1-1-2-3)空いてなくて既存キーしていない // →別のキーが既にいる(衝突)ので、次のアドレスに移動 // →何もせず(index++に戻り次のアドレスをチェック) } } //#################### オープンアドレス方END #################### //(3)値の追加処理 //(3-1)インデックスの要素がnullの場合 // →未使用の要素のため、例外回避のため0を返却 if(wordmix[index]==null) { System.out.println("### [String]="+w+" [index]="+index); return 0; } //(3-2)インデックスの要素がnullでない場合 // →使用中の既存要素のため、現在の値(Value)を返却 else { System.out.println("@@@ [String]="+w+" [index]="+index); return wordmix[index].getValue(); } } //【クラス】Wordクラス class Word { private String word; //Key private int wordcode; //Value public Word(String w, int c) {word = w; wordcode = c;} public String getKey() {return word;} public int getValue() {return wordcode;} public void setValue(int c) {wordcode = c; } } }
(図132)実行結果
(1-4) 参考(オープンアドレス法を適用する前のプログラム)
参考ですが、オープンアドレス法を適用する前のプログラムもご紹介します。
(※上記のプログラムで「## オープンアドレス方START ##」~「## オープンアドレス方END ##」の部分を削除しただけです)
こちらはオープンアドレス法を適用していないので、先ほどの例の「rumble」と「fairly」が衝突している様子などを見る事ができます。
public class IT0XXX_OpenAddress_Before { public static void main(String args[]) { CustomHashMap3 cm = new CustomHashMap3(1000); cm.put("rumble", 15); //index=369 cm.put("fairly", 20); //index=369 cm.put("severe", 10); //index=372 cm.put("stuff", 35); //index=372 System.out.println("Point of team rumble : "+cm.get("rumble")); System.out.println("Point of team fairly : "+cm.get("fairly")); System.out.println("Point of team severe : "+cm.get("severe")); System.out.println("Point of team stuff : "+cm.get("stuff")); } } class CustomHashMap3 { //【メンバ変数】 public Word[] wordmix; int mapsize; //【コンストラクタ】CustomHashMapのコンストラクタ public CustomHashMap3(int mapsize) { this.wordmix = new Word[mapsize]; this.mapsize = mapsize; } //【値の更新】Wordクラスのインスタンスを更新 public void put(String w, int c) { //単語のインデックスを計算 int index = (w.hashCode() & 0x7fffffff) % mapsize; //インデックスの要素がnullの場合 →新規追加 if(wordmix[index]==null) { wordmix[index] = new Word(w,c); } //インデックスの要素がnullでない場合 →既存更新 else { wordmix[index].setValue(c); } } //【値の取得】キーからインデックスを計算して値を取得 public int get(String w) { int index = (w.hashCode() & 0x7fffffff) % mapsize; //インデックスの要素がnullの場合 →未使用の要素のため0を返却 if(wordmix[index]==null) { System.out.println("### [String]="+w+" [index]="+index); return 0; } //インデックスの要素がnullでない場合 →使用中の要素のためカウンタの値を返却 else { System.out.println("@@@ [String]="+w+" [index]="+index); return wordmix[index].getValue(); } } //【クラス】Wordクラス class Word { private String word; //Key private int wordcode; //Value public Word(String w, int c) {word = w; wordcode = c;} public String getKey() {return word;} public int getValue() {return wordcode;} public void setValue(int c) {wordcode = c; } } }
(図131)
>目次にもどる