web-dev-qa-db-ja.com

ハッシュテーブルの基礎?

ハッシュテーブルの基本概念についてはかなり混乱しています。ハッシュをコーディングする場合、どのように始めますか?ハッシュテーブルと通常の配列の違いは何ですか?

基本的に誰かがこの質問に答えた場合、私の質問はすべて答えられると思います:ランダムに生成された100個の数字(キーとして)がある場合、ハッシュテーブルをどのように実装しますか?なぜ配列よりも有利ですか?

擬似コードまたはJavaは学習ツールとして高く評価されます...

54
kylex

これまでの回答は、ハッシュテーブルを定義し、いくつかの理論を説明するのに役立ちましたが、例を参考にすると、より良い感じが得られると思います。

ハッシュテーブルと通常の配列の違いは何ですか?

ハッシュテーブルと配列はどちらも、データを保存および取得できる構造です。両方とも、indexを指定し、それに関連付けられた値を取得できます。 Daniel Spiewakが指摘したように、配列のインデックスはsequentialですが、ハッシュテーブルのインデックスはの値に基づいているという違いがありますそれらに関連付けられたデータ

なぜハッシュテーブルを使用するのですか?

ハッシュテーブルは、大量のデータ、特に簡単には検索できないデータのアイテムを検索する非常に効率的な方法を提供できます。 (ここで「大」とは、 ginormous を意味します。つまり、順次検索を実行するのに時間がかかるという意味です)。

ハッシュをコーディングする場合、どのように始めますか?

問題ない。最も簡単な方法は、データに対して実行できる任意の数学演算を発明することです。これは、数値N(通常は整数)を返します。次に、その番号を「バケット」の配列へのインデックスとして使用し、データをバケット#Nに保存します。秘Theは、後で簡単に見つけられるように値を異なるバケットに配置する傾向がある操作を選択することです。

例:大型モールでは、顧客が駐車した場所を覚えやすくするために、顧客の車と駐車場のデータベースを保持しています。データベースには、makecolorlicense plate、およびparking location。店を出ると、買い物客はそのメーカーと色を入力して自分の車を見つけます。データベースは、ナンバープレートと駐車スペースの(比較的短い)リストを返します。クイックスキャンで買い物客の車の位置を特定します。

SQLクエリでこれを実装できます。

SELECT license, location FROM cars WHERE make="$(make)" AND color="$(color)"

データが本質的に単なるリストである配列に格納されている場合、一致するすべてのエントリについて配列をスキャンすることでクエリを実装することを想像できます。

一方、ハッシュルールを想像してください。

メイクと色のすべての文字のASCII文字コードを追加し、100で除算し、残りをハッシュ値として使用します。

このルールは、各アイテムを0〜99の数に変換し、基本的にデータを100バケットにソートします。顧客が車を見つける必要があるたびに、メーカーと色をハッシュして、情報を含む100個のうちoneバケットを見つけることができます。検索はすぐに100分の1に削減されました!

次に、数十の基準に基づいて検索される数百万のエントリを持つデータベースなど、膨大な量のデータに例を拡張します。 「適切な」ハッシュ関数は、追加の検索を最小限に抑え、かなりの時間を節約する方法でデータをバケットに分散します。

68
Adam Liss

まず、ハッシュ関数とは何かを理解する必要があります。ハッシュ関数は、キー(たとえば、任意の長さの文字列)を受け取り、可能な限り一意の数値を返す関数です。同じキーは常に同じハッシュを返す必要があります。 Javaのような単純な文字列ハッシュ関数は

public int stringHash(String s) {
    int h = s.length();
    for(char c : s.toCharArray()) {
        h ^= c;
    }
    return h;
}

http://www.azillionmonkeys.com/qed/hash.html で適切なハッシュ関数を学習できます。

現在、ハッシュマップはこのハッシュ値を使用して値を配列に配置します。 Simplistic Javaメソッド:

public void put(String key, Object val) {
    int hash = stringHash(s) % array.length;
    if(array[hash] == null) {
        array[hash] = new LinkedList<Entry<String, Object> >();
    }
    for(Entry e : array[hash]) {
        if(e.key.equals(key)){
            e.value = val;
            return;
        }
    }
    array[hash].add(new Entry<String, Object>(key, val));
}

(このマップは一意のキーを強制します。すべてのマップが強制するわけではありません。)

2つの異なるキーが同じ値にハッシュすること、または2つの異なるハッシュが同じ配列インデックスにマップすることが可能です。これに対処するための多くのテクニックがあります。最も簡単な方法は、各配列インデックスにリンクリスト(またはバイナリツリー)を使用することです。ハッシュ関数が十分であれば、線形検索は必要ありません。

キーを検索するには:

public Object get(String key) {
    int hash = stringHash(key) % array.length;
    if(array[hash] != null) {
        for(Entry e : array[hash]) {
            if(e.key.equals(key))
                return e.value;
        }
    }

    return null;
}
46
gnud

ハッシュテーブルはassociativeです。これは、単なる線形データ構造である配列とは大きな違いです。配列を使用すると、次のようなことができます。

int[] arr = ...
for (int i = 0; i < arr.length; i++) {
    System.out.println(arr[i] + 1);
}

正確なメモリオフセット(i)を指定して、配列から要素を取得していることに注意してください。これは、後でキーに基づいて値を取得するキー/値ペアを保存できるハッシュテーブルとは対照的です。

Hashtable<String, Integer> table = new Hashtable<String, Integer>();
table.put("Daniel", 20);
table.put("Chris", 18);
table.put("Joseph", 16);

上記の表を使用して、次の呼び出しを行うことができます。

int n = table.get("Chris");

...そしてn18

これはおそらくあなたの質問のほとんどに答えると思います。ハッシュテーブルの実装は非常に興味深いトピックであり、1つは Wikipediaがまずまずに対処しています です。

17
Daniel Spiewak

「ハッシュテーブルがキーを検索する方法とキーの生成方法にもっと興味があります。」

  1. ハッシュは、キーオブジェクトを数値に変換します。これは「ハッシュ」と呼ばれ、オブジェクトからハッシュを作成します。 ハッシュ関数 を参照してください。たとえば、文字列のバイトを合計することは、標準のハッシュ手法です。 2を法とする和を計算します32 ハッシュを管理可能なサイズに保つため。ハッシュは常に同じ答えを与えます。これは[〜#〜] o [〜#〜](1)です。

  2. 数字は、HashTableの「スロット」を示します。任意のキーオブジェクトを指定すると、ハッシュ値はハッシュ値を計算します。ハッシュ値は、テーブル内のスロットを提供します。通常、mod( hash, table size )。これも[〜#〜] o [〜#〜](1)です。

それが一般的な解決策です。 2つの数値計算で、キーとしての任意のオブジェクトから値としての任意のオブジェクトに移動しました。これほど高速なものはほとんどありません。

オブジェクトからハッシュ値への変換は、これらの一般的な方法のいずれかで行われます。

  1. 4バイトの「プリミティブ」オブジェクトの場合、オブジェクトのネイティブ値は数値です。

  2. オブジェクトのアドレスは4バイトであり、オブジェクトのアドレスはハッシュ値として使用できます。

  3. 単純な ハッシュ関数 (MD5、SHA1、何でも)は、オブジェクトのバイトを累積して4バイトの数値を作成します。高度なハッシュは単純なバイトの合計ではなく、単純な合計は元の入力ビットのすべてを十分に反映していません。

ハッシュテーブルのスロットはmod(number、size of table)です。

そのスロットに目的の値があれば、完了です。それが望ましい値でない場合は、別の場所を調べる必要があります。テーブル内の空きスポットを探すための一般的なプローブアルゴリズムがいくつかあります。線形は、次の空きスポットの単純な検索です。 Quadraticは、空きスロットを探すための非線形ホッピングです。乱数ジェネレーター(固定シードを使用)を使用して、データを均一に任意に拡散する一連のプローブを生成できます。

探索アルゴリズムは[〜#〜] o [〜#〜](1)ではありません。テーブルが十分に大きければ、衝突の可能性は低く、プローブは関係ありません。テーブルが小さすぎる場合、衝突が発生し、プローブが発生します。その時点で、プローブとテーブルサイズのバランスを取り、パフォーマンスを最適化する「チューニングと調整」の問題になります。通常、テーブルを大きくします。

ハッシュテーブル を参照してください。

8
S.Lott

まだ具体的に言及されていなかったもの:

配列でハッシュテーブルを使用するポイントはパフォーマンスです。

配列を反復処理するには、通常、O(1)からO(x)のxが配列内のアイテムの数です。あなたのアイテムを見つけることは非常に変数になります、特に配列内の何十万ものアイテムについて話している場合。

適切に重み付けされたハッシュテーブルは、通常、ハッシュテーブル内のアイテムの数に関係なく、ほとんど定数 O(1)を超えるアクセス時間を持ちます。

6
CodingWithSpike

ランダムに生成された100個の数値に対してハッシュテーブルを使用することは望ましくありません。

ハッシュテーブルについて考える良い方法は、値のペアについて考えることです。学生を使用して、全員に学生ID番号があるとしましょう。プログラムでは、学生に関する情報(名前、電話番号、請求書など)を保存します。基本情報(名前や学生IDなど)のみを使用して、学生に関するすべての情報を検索する必要があります。

10,000人の生徒がいるとしましょう。それらをすべて配列に格納する場合、各エントリの学生IDを探している配列と比較して、配列全体をループする必要があります。

代わりに、学生ID番号を配列内の位置に「ハッシュ」(以下を参照)する場合、番号が同じハッシュを持つ学生の人だけを検索する必要があります。必要なものを見つけるための作業がはるかに少なくなります。

この例では、学生IDが6桁の数字であるとします。ハッシュ関数は、数字の下3桁のみを「ハッシュキー」として使用できます。したがって、232145は配列の位置145にハッシュされます。したがって、必要なのは999要素の配列のみです(各要素は学生のリストです)。

それはあなたにとって良いスタートになるはずです。もちろん、この種の情報については、教科書またはウィキペディアを読む必要があります。しかし、私はあなたがすでにそれをし、読書にうんざりしていると思います。

4
SoapBox

要するに、ハッシュテーブルの仕組みです。

本がいっぱいある図書館があると想像してください。本を配列で保管する場合、各本を棚のある場所に置き、誰かが本を見つけるように頼んだら、すべての棚に目を通します-かなり遅いです。誰かが「book#12345」と言ったとしても、かなり簡単に見つけることができます。

代わりに、本のタイトルが「A」で始まる場合、行1に移動するとします。2番目の文字が「B」の場合、行1、ラック2に移動します。3番目の文字が「C」の場合、本の位置を特定するまで、1行目、ラック2、棚3 ...に続きます。次に、本のタイトルに基づいて、どこにあるべきかを正確に知ることができます。

さて、私が説明した単純な「ハッシュ」アルゴリズムにはいくつかの問題があります。一部の棚はかなり過負荷になり、他の棚は空になり、一部の本は同じスロットに割り当てられます。そのような問題を避けるようにしてください。

しかし、これは基本的な考え方です。

3
SquareCog

ハッシュテーブルと配列の違いについてその部分に答えます...しかし、インポートのハッシュアルゴリズムを実装したことがないので、それをもっと知識のある人に任せます:)

配列は、オブジェクトの順序付きリストにすぎません。オブジェクト自体は実際には重要ではありません...重要なのは、オブジェクトを挿入順にリストしたい場合、常に同じであるということです(つまり、最初の要素alwaysのインデックスは0)。

ハッシュテーブルについては、順序ではなくキーでインデックス付けされています...ハッシュアルゴリズムの基本的な検索は、私ができる以上の洞察をあなたに与えると思います...ウィキペディアには非常にまともなものがあります...キーとして使用される任意のオブジェクトをすばやく取得するためにキーが使用されること。

利点に関して:挿入の順序が重要な場合、配列またはある種の順序付きリストが必要です。任意のキー(さまざまなハッシュ関数によってキー設定)による高速ルックアップが重要な場合、ハッシュテーブルが意味をなします。

0
Jason Coco

[これは上記のme.yahoo.com/aによるコメントに対する返信です]

それはハッシュ関数に依存します。ハッシュ関数がWordの長さに従ってWordをハッシュすると、chrisのキーは5になります。同様に、yahooのキーも5になります。 5)によってキー設定された「バケット」内。この方法では、配列をデータのサイズと等しくする必要はありません。

0
ashokgelal

ハッシュテーブルは、クイックルックアップ用に作成されるデータ構造です。

エントリの数が非常に少ない場合、ハッシュテーブルは効果的ではありません。

参照

いくつかの例:

    import Java.util.Collection;
    import Java.util.Enumeration;
    import Java.util.Hashtable;
    import Java.util.Set;

    public class HashtableDemo {

    public static void main(String args[]) {

// Creating Hashtable for example

     Hashtable companies = new Hashtable();


// Java Hashtable example to put object into Hashtable
// put(key, value) is used to insert object into map

     companies.put("Google", "United States");
     companies.put("Nokia", "Finland");
     companies.put("Sony", "Japan");


// Java Hashtable example to get Object from Hashtable
// get(key) method is used to retrieve Objects from Hashtable

     companies.get("Google");


// Hashtable containsKey Example
// Use containsKey(Object) method to check if an Object exits as key in
// hashtable

     System.out.println("Does hashtable contains Google as key: "+companies.containsKey("Google"));


// Hashtable containsValue Example
// just like containsKey(), containsValue returns true if hashtable
// contains specified object as value

      System.out.println("Does hashtable contains Japan as value: "+companies.containsValue("Japan"));


// Hashtable enumeration Example
// hashtabl.elements() return enumeration of all hashtable values

      Enumeration enumeration = companies.elements();

      while (enumeration.hasMoreElements()) {
      System.out.println("hashtable values: "+enumeration.nextElement());
      }


// How to check if Hashtable is empty in Java
// use isEmpty method of hashtable to check emptiness of hashtable in
// Java

       System.out.println("Is companies hashtable empty: "+companies.isEmpty());


// How to find size of Hashtable in Java
// use hashtable.size() method to find size of hashtable in Java

      System.out.println("Size of hashtable in Java: " + companies.size());


// How to get all values form hashtable in Java
// you can use keySet() method to get a Set of all the keys of hashtable
// in Java

      Set hashtableKeys = companies.keySet();


// you can also get enumeration of all keys by using method keys()

      Enumeration hashtableKeysEnum = companies.keys();


// How to get all keys from hashtable in Java
// There are two ways to get all values form hashtalbe first by using
// Enumeration and second getting values ad Collection

      Enumeration hashtableValuesEnum = companies.elements();


      Collection hashtableValues = companies.values();


// Hashtable clear example
// by using clear() we can reuse an existing hashtable, it clears all
// mappings.

       companies.clear();
      }
     }

出力:

Does hashtable contains Google as key: true

Does hashtable contains Japan as value: true

hashtable values: Finland

hashtable values: United States

hashtable values: Japan

Is companies hashtable empty: false

Size of hashtable in Java: 3
0
Durai Amuthan.H

この質問は、今ではかなり明確に、さまざまな方法で答えられていると思います。

別の視点を追加したいだけです(新しい読者も混乱させるかもしれません)

抽象度が最も低いレベルでは、配列は単なる連続したメモリブロックです。単一の要素の開始アドレス(startAddress)、サイズ(sizeOfElement)、およびindexを指定すると、要素のアドレスは次のように計算されます。

elementAddress = startAddress + sizeOfElement * index

ここで注目すべき興味深いことは、配列をキーとしてindexと、O(1)

0
Omer Akhter