Transcript 第13回講義
第13回 ハッシュテーブルを
使ったプログラム
~高速に検索するには?~
学習目標
ハッシュテーブルの概念を説明できる
ハッシュテーブルの利点・欠点を説明できる
検索におけるkeyとvalueの関係を説明できる
簡単なハッシュテーブルの仕組みを説明でき
る
ハッシュテーブルを実装できる
汎用的なハッシュテーブルを実装できる
13.1 ハッシュテーブルとは
13.1.1 検索と効率
13.1.2 ハッシュテーブル概要
13.1.3 keyとvalue
13.1.1 検索と効率
リニアサーチ
バイナリサーチ
O(N)
O(logN)
ハッシュテーブル→
O(1)
13.1.2 ハッシュテーブル概要
配列の番地にそのまま入れておけば、直
接検索できる
商品番号1001のコーラは配列の1001番地に
入れておく
配列番号
0
1
2
3
インスタンス
1000
・・・・・
1001
1002
1003
コーラ
13.1.3 keyとvalue
key
検索するためのキー
value
格納されている値
key
1001
1002
1003
1004
value
colaObject
sodaObject
chaObject
ddObject
13.2 ハッシュテーブルの仕組み
13.2.1
13.2.2
13.2.3
13.2.4
名前をキーに検索できるようにする
ハッシュ値を求める
番地の衝突を回避する
ハッシュテーブルの利点・欠点
13.2.1 名前をキー
に検索できるようにする
名前をキーにしよう
Idで商品種類を扱う
のはややこしい
ハッシュテーブルは
文字列のキーが得
意
key
value
”コー
ラ”
colaObject
”ソー
ダ”
sodaObject
”お茶”
”DD”
chaObject
ddObject
13.2.2 ハッシュ値を求める
①名前を数字に変換する
②配列の番号と対応させる
③小さい配列で済むようにするには
①名前を数字に変換する
インデックシング
文字として扱っている間は、コンピュータでは
検索できないので、何らかの数字に変換する
例えば、こんなやり方があります(詳しくは専門書を参照)
変換表
コ 98
- 103
ラ 67
コーラ
→
9810367
Javaでハッシュ値を求める
JavaではObjectクラスにhashcode()メソッ
ドがあるので、どんなオブジェクトでも、簡
単に求めることができます
String cola = "コーラ";
int hashCode = cola.hashCode();
もちろんStringクラ
スもObjectクラスを
継承しているよ
②配列の番号と対応させる
例えば、コーラを9810367という数字に変
換したら対応する配列の番地に挿入する
こうしておけば、1回で検索できる
1
2
3
4
9810366
・・・・・
9810367
コーラ
9810368
9810369
しかし、そんなに大きな配列を作るのは無駄
③小さい配列で
済むようにするには
例えば1000要素の配列で済ませることを
考える
1000で割ったあまりを求める
コーラ
→
1
2
3
4
9810367
→
367
366
・・・・・
367
368
369
コーラ
・・・・・
13.2.3 番地の衝突を回避する
①番地の衝突
②衝突回避(1):空き番地法
③空き番地法の問題点
④衝突回避(2):分離連鎖法
①番地の衝突
違うオブジェクトが同じハッシュ値になる可
能性がある
コーラ
ソーダ
→
→
31256448
587648448
→
→
448
448
②衝突回避(1):空き番地法
もし計算された番地にすでにデータが格納されて
いた場合次の番地にデータを格納する
次も格納されていたらさらに次の番地に格納しようと
する
検索するときにはハッシュ値を求めた後、その番
地から順に配列の中身をリニアサーチで調べる
1
2
3
4
ソーダも入りた
い!
447
・・・・・
448
コーラ
449
ソーダ
500
DDレモン
空き番地法のリスト(追加)
//新たに商品種類を追加する
//配列がいっぱいではないと信じる
//同じkeyが存在していないことを信じる
public void add(ItemType value){
String key = value.getName();
//商品名をキーとする
//ハッシュ値を求める
int hashCode = key.hashCode();
int arrayLoc = hashCode % ARRAY_SIZE;
例題13-1
(ItemTypeList.java)
#add
//ハッシュした番地から、空のセルを探す
while(true){
if(itemTypeArray[arrayLoc] == null){//空のセルがあった
itemTypeArray[arrayLoc] = value;//空のセルに商品種類を追加する
return;
}
//空のセルがなかったら
arrayLoc++;//次の項目へ
if(arrayLoc >= ARRAY_SIZE ){//必要ならラップアラウンド
arrayLoc = 0;
}
}
}
空き番地法のリスト(検索)
//商品種類を商品名をキーに検索する
public ItemType search(String key){
//ハッシュ値を求める
int hashCode = key.hashCode();
int arrayLoc = hashCode % ARRAY_SIZE;
//空のセルになるまで順番に探す
while(itemTypeArray[arrayLoc] != null){
if(itemTypeArray[arrayLoc].getName().equals(key)){
//Keyが等しい商品が見つかった
return itemTypeArray[arrayLoc];
}
arrayLoc++;//次の項目へ
if(arrayLoc >= ARRAY_SIZE ){//必要ならラップアラウンド
arrayLoc = 0;
}
}
return null;//見つからなかった
}
例題13-1
(ItemTypeList.java)
#search
③空き番地法の問題点
クラスター化
集中して埋まってしまう個所
ができてしまう
1
2
3
4
5
6
7
リニアサーチが長くなって効率が
落ちる
8
9
10
11
12
13
クラスター化を防ぐ
平方探索
ダブルハッシュ
重複した時に、次の番地ではなく1,4,9,16こ先
と少し離れた場所に置く
第2種クラスター化
キーの値によって次の番地を決める
分離連鎖法
次に説明する方法
④衝突回避(2):分離連鎖法
衝突したら、その番地からさらに連結リストを使っ
てデータを格納する
検索するときにはハッシュ値を求めた後、その番
地から順に連結リストを使って調べる
1
2
3
4
447
・・・・・
448 コーラ
449
500
ソーダ
DDレモン
分離連鎖法のリスト(追加)
//同じkeyが存在していないことを信じる
public void add(ItemType value){
String key = value.getName();//商品名をキーに設定する
例題13-2
(ItemTypeList.java)
//ハッシュ値を求める
int hashCode = key.hashCode();
int arrayLoc = hashCode % ARRAY_SIZE;
LinkObject insertLink = new LinkObject(value);
if (itemTypeLinkArray[arrayLoc] == null){
//ハッシュした番地が未使用だったとき
itemTypeLinkArray[arrayLoc] = insertLink;//追加
}else{
//ハッシュした番地が使用済みだったとき
LinkObject current = itemTypeLinkArray[arrayLoc];//現在検索中のリンク
while (true){
if(current.getNext() == null){
//連結リストの最後が見つかった
current.setNext(insertLink);
break;
}else{
//次のリンクを検索中のリンクに設定
current = current.getNext();
}
}
}
}
分離連鎖法のリスト(検索)
//商品種類を商品名をキーにして検索する
public ItemType search(String key){
//ハッシュ値を求める
int hashCode = key.hashCode();
int arrayLoc = hashCode % ARRAY_SIZE;
例題13-2
(ItemTypeList.java)
#search
LinkObject current = itemTypeLinkArray[arrayLoc];//現在検索中のリンク
while (current != null){
//ハッシュした番地に調査していないリンクオブジェクトが残っている
if(current.getValue().getName().equals(key)){
//検索対象が見つかった
return current.getValue();
}else{
//次のリンクを検索中のリンクに設定
current = current.getNext();
}
}
//ハッシュした番地にリンクオブジェクトがなかったとき
return null;//見つからなかった
}
13.2.4
ハッシュテーブルの利点・欠点
考えてみよう
ハッシュテーブルの利点・欠点
利点
検索が速い → O(1)
名前でも速く検索できる
バイナリサーチでも工夫すればできる
欠点
あるキーによる検索しかできない
名前をキーにしたら、idでの検索はリニアサーチ
データの保持に内部的には配列を使用するので、事
前に用意した配列が満杯に近づいてくると検索、挿入
の効率が格段に落ちる
保持するデータを昇順や降順にソートして取り出すと
いった機能はない
13.3 汎用的なハッシュテーブル
13.3.1 汎用的なハッシュテーブルの実装
13.3.2 JavaAPIを利用する
13.3.1 汎用的な
ハッシュテーブルの実装
第12回で作った汎用的なListのように汎用
的なハッシュテーブルを作ることを考える
分離連鎖法で実装する
商品種類リスト
商品種類
- 名前
- 商品番号
- 価格
0..n
1
+ 追加(商品種類 newItemType)
+ 削除(商品種類 deleteItemType)
+ 検索(String 名前)
汎用Hashtable
+ 追加(Object key, Object value)
+ 削除(Object key)
+ 検索(Object key)
ObjectHashTable
インターフェイス
void put(Object key,Object value)
Object get(Object key)
与えられたkeyで検索する
void remove(Object key)
keyとvalueの組を追加する
与えられたkeyとvalueの組を削除する
Object[] keys()
displayメソッドのために、すべてのキーを取得する
ハッシュテーブルのリスト
/**
* 連結リストオブジェクトクラス
*/
public class LinkObject {
private Object key;
private Object value;
private LinkObject next;
例題13-3
(LinkObject.java)
//キー
//値
//次の要素への参照
/**
* コンストラクタ
*/
public LinkObject(Object newKey,Object newValue){
key = newKey;
value = newValue;
}
…続く
ハッシュテーブルのリスト(追加)
//キーと値のペアをハッシュテーブルに格納する
public void put(Object key,Object value){
//ハッシュ値を求める
int hashCode = key.hashCode();
int arrayLoc = hashCode % ARRAY_SIZE;
//追加する
LinkObject insertLink = new LinkObject(key,value);
if (itemTypeLinkArray[arrayLoc] == null){
//ハッシュした番地が未使用だったとき->そのまま追加
itemTypeLinkArray[arrayLoc] = insertLink;
size++;//追加したのでHashTableのサイズを一つ増やす
return;
例題13-3
(ObjectHashTable.java)
#add
}else{
//ハッシュした番地が使用済みだったとき->連結リストに追加
LinkObject current = itemTypeLinkArray[arrayLoc];//現在検索中のリンクを設定する
//連結リストの最後を探す
while (true){
if(current.getNext() == null){
//連結リストの最後が見つかった
current.setNext(insertLink);//最後に連結する
size++;//追加したのでHashTableのサイズを一つ増やす
return;//連結完了
}else{
//見つからない
current = current.getNext();//次のリンクを検索中のリンクに設定
}
}
}
13.3.2 JavaAPIを利用する
ハッシュテーブルも良く使われるので、用
意されている
Map
keyとvalueでデータを格納する
クラスのインターフェイス
HashMap
Hashtableを使ってMapを実装
したクラス
(実は、Hashtableを使わなくても
keyとvalueでデータを格納する方法がある)
MapクラスのAPI
ObjectHashtableと同じメソッド
void put(Object key,Object value)
Object get(Object key)
void remove(Object key)
ObjectHashtableと異なるメソッド
Set keySet()
すべてのキーをSetというインターフェイスで取得する
Setとは重複のないListのこと
得られたSetに対し、toArray()を呼べばObjectHashtableと同
様の配列が得られる
Map map = new HashMap();
Object keys[] = map.keySet().toArray();