ハッシュ テーブル

このセクションでは、Curl® 言語のハッシュ テーブルについて説明します。具体的には、以下の項目について説明します。

ハッシュ テーブルの概要

ハッシュ テーブルは、キーと要素のペアのコレクションです。要素にアクセスするには、キーを使用します。キーにはあらゆるデータ型が含まれます。たとえば、キーは識別番号を表す整数、または名前を表す文字列である可能性があります。ハッシュ テーブルにある要素は順序付けされていません。次の図は、3 つの要素を持つハッシュ テーブルを示します。文字列キーは果物の種類を示し、整数要素はその果物の値段を示します。果物の値段を決定するには、キー (果物の名前) を使用して、要素 (果物の値段) を取得します。


ハッシュ テーブルに追加する各要素について、ハッシング アルゴリズムとキーの値を使用して、キーと要素を格納する実際の位置を決定します。実際にはキーをある配列に格納し、要素を別の配列に格納します。ハッシング アルゴリズムをキーの値に適用し、キーと要素を格納するこれらの配列のインデックスを決定します。このインデックスは、キーのハッシュ値と呼ばれます。ハッシング アルゴリズムでハッシュ値が均等に配分されるのが理想的です。ハッシング アルゴリズムで複数のキーに同じハッシュ値が与えられると衝突が起きます。ハッシング アルゴリズムでは、要素を別の位置に置いてこの衝突を解消します。
注意: コレクションを反復処理中にコレクションの内容が変更される場合は、その結果は定義されません。

ハッシュ テーブルの作成

HashTable-of クラスを使用して、ハッシュ テーブルを宣言できます。HashTable-of クラスはパラメータ化クラスです。クラスを指定する際に、2つのパラメータを指定する必要があります。1つはハッシュ テーブルのキーのデータ型、もう1つは要素のデータ型を表すパラメータです。たとえば、次のコードは String 型のキーと int 型の要素をもつハッシュ テーブルを宣言しています。
{let my-hash-table:#{HashTable-of String, int}}
注意: 上の宣言にある # は、変数が null (つまり、値を持たない) であるか、ハッシュ テーブルであるかを指定します。変数が null 値になる場合は、宣言で # を指定する必要があります。指定されていない場合、RTE は NullDereferenceException をスローします。
ハッシュ テーブルのインスタンスを作成する際にも、キーと要素のデータ型を表すパラメータを指定する必要があります。ハッシュ テーブルのインスタンスを作成するには、最初のパラメータとしてパラメータ化されたクラスの仕様を、残余引数として初期のキーと要素のペアを提供して、new を呼び出します。RTEはハッシュ テーブルを作成し、提供されたキーと要素のペアを格納します。たとえば、次のコードは文字列のキーと整数の要素を持つハッシュ テーブルを作成します。中には、キーと要素のペアが三つあります。Apple キーは要素の 56 と、Banana キーは要素の 87 と、そして Cherry は要素の 34 と関連付けられています。
{let my-hash-table:{HashTable-of String, int} = 
    {new {HashTable-of String, int},
        "Apple", 56,
        "Banana", 87,
        "Cherry", 34
    }
}
他のオブジェクトと同様、クラスのインスタンスの作成時には new キーワードの使用を推奨しますが、オプションになります。new キーワードを使用しない場合は、マクロ呼び出しと残余引数との間のカンマも使用しないようにしてください。次に例を示します。
{let my-hash-table:{HashTable-of String, int} = 
    {{HashTable-of String, int}
        "Apple", 56,
        "Banana", 87,
        "Cherry", 34
    }
}

要素へのアクセス

コンテナ for ループを使用して、ハッシュ テーブルのコンテンツにアクセスすることができます。「コンテナ ループ」を参照してください。コンテナ ループの3つの形式で要素、キー、あるいはキーと要素のペアによってハッシュ テーブルにアクセスできます。次の例ではこれら3つの形式をすべて使います。
次に例を示します。

例: ハッシュ テーブルにある要素へのアクセス
{value
    || Declare and initialize a hash table.
    let my-hash-table:{HashTable-of String, int} =
        {new {HashTable-of String, int},
            "Apple", 56,
            "Banana", 87,
            "Cherry", 34
        }
    let elements:VBox = {VBox}
    let keys:VBox = {VBox}
    let both:VBox = {VBox}
    || For each element in the hash table, add the element
    || to a VBox.
    {for element:int in my-hash-table do
        {elements.add element}
    }
    || For each key in the hash table, add the key
    || to a VBox.
    {for key k:String in my-hash-table do
        {keys.add k}
    }
    || For each pair in the hash table, add the both
    || to a VBox.
    {for e:int key k:String in my-hash-table do
        {both.add 
            {HBox 
                spacing = 5pt,
                e,
                k
            }
        }
    }
    {spaced-hbox            
        "By element: ",
        elements,
        {Fill width = 1cm},
        "By key: ",
        keys,
        {Fill width = 1cm},
        "By both: ",
        both
    }
}
HashTable-of.getAssociation-of.get を継承し、いかなる関連付けからも要素を取得する汎用メソッドです。HashTable-of.get-if-exists メソッドを使用すると、キーの与えられたハッシュ テーブル内の要素にアクセスできます。このメソッドは関連付けられた要素と、ハッシュ テーブル内でキーが見つかったかどうかを示すブール値を返します。

例: キーを与えられた要素へのアクセス
{let my-hash-table:{HashTable-of String, int} =
    {new {HashTable-of String, int},
        "Apple", 56,
        "Banana", 87,
        "Cherry", 34
    }
}
{let output:#Frame}
{let k:String = "Cherry"}
{let (e:int, exists?:bool) = {my-hash-table.get-if-exists k}}
{if exists? then
    set output = 
        {Frame
            {text The key "{value k}" retrieves element {value e}}
        }
 else
    let output = 
        {Frame
            {text The key "{value k}" is not found}
        }
}
{value output}
Association-of.get-key はキーの値を与えられたキーを返します。HashTable-of.get-key-if-exists メソッドを使用すると、キーの値を与えられたハッシュ テーブルにアクセスでき、メソッドはキーが見つかったかどうかを示すブール値を返します。このメソッドでハッシュ テーブルにキーが存在するかどうかを判定することができます。ブール値を返すだけの HashTable-of.key-exists? メソッドにも注目してください。
次の例では、キーの値が "Orange" の要素を追加します。(そのキーがまだ使われていない場合)

例: キーが存在するかどうかの判定
{value
    let my-hash-table:{HashTable-of String, int} =
        {new {HashTable-of String, int},
            "Apple", 56,
            "Banana", 87,
            "Cherry", 34
        }
    {let my-key:String = "Orange"}
    let (k:#String, exists?:bool) = {my-hash-table.get-key-if-exists my-key}
    {if not exists? then
        {my-hash-table.set my-key, 62}
    }
    let keys:VBox = {VBox}
    {for key k:String in my-hash-table do
        {keys.add k}
    }
    keys
}

要素の追加

HashTable-of.set メソッドを使用して、次の例のように、新しいキーと要素のペアをハッシュ テーブルに追加することができます。

例: ハッシュ テーブルへの要素の追加
{let my-hash-table:{HashTable-of String, int} = 
{new {HashTable-of String, int},
"Apple", 56,
"Banana", 87,
"Cherry", 34
}
}
{my-hash-table.set "Orange", 62}
{text You added element {my-hash-table.get "Orange"} 
with key "Orange"}

要素の削除

ハッシュ テーブルから要素を削除するには、すべての要素を削除する HashTable-of.clear、または指定された1つの要素を削除する HashTable-of.remove を使用します。
次の例では、ハッシュ テーブル内にキーの値のリストを作成し、その後このリストを使用してキーと要素のペアを削除します。また、ハッシュ テーブルが空であることを示すために、HashTable-of.size プロパティを使用します。

例: ハッシュ テーブルからの要素の削除
{let my-hash-table:{HashTable-of String, int} = 
{new {HashTable-of String, int},
"Apple", 56,
"Banana", 87,
"Cherry", 34
}
}
{let keys:{Array-of String} = {new{Array-of String}}}
{for key k:String in my-hash-table do
{keys.append k}
}
{CommandButton
label = "Empty Hash Table",
{on Action do
{for key:String in keys do
{my-hash-table.remove key}
}
{popup-message 
{text Size of hash table: {value my-hash-table.size}}
}
}
}

ハッシュ テーブルのクローン作成

クローンはクローン化されたオブジェクトと同じデータ型を持つ新しいオブジェクトです。ハッシュ テーブルのようなオブジェクトは格納する子オブジェクトへのポインタを持っています。クローンを作成する際に、それらのポインタのコピーを作成します。要素の追加、削除、および置換によって行われた変更はその操作を行ったテーブルだけを変更します。
次の例では HashTable-of.set を使用して、キーの値 "Frank" に関連付けられた電話番号を置き換えます。このメソッドはオリジナルの StringBuf オブジェクトを異なるオブジェクトに置き換えます。オリジナルのハッシュ テーブルでは変更が見られますが、クローンではそうではないことに注目してください。

例: ハッシュ テーブルのコピー
{let my-hash-table:{HashTable-of String, StringBuf} =
{new {HashTable-of String, StringBuf},
"John", {StringBuf "781-555-1234"},
"Paul", {StringBuf "781-555-5678"},
"Frank", {StringBuf "781-555-4321"}
}
}
{let new-hash-table:{HashTable-of String, StringBuf} = 
{my-hash-table.clone}
}
{let table1:Table = {Table columns = 2}}
{let table2:Table = {Table columns = 2}}
{let sb:StringBuf = {StringBuf "781-555-9999"}}
{my-hash-table.set "Frank", sb}

{for e:StringBuf key k:String in my-hash-table do
{table1.add k}
{table1.add e}
}
{for e:StringBuf key k:String in new-hash-table do
{table2.add k}
{table2.add e}
}
{HBox
"Original:",
{Fill width = .5cm},
table1,
{Fill width = .5cm},
"Clone:",
{Fill width = .5cm},
table2
}
オリジナルのハッシュ テーブルとクローンの両方が指し示す基本データを変更する場合、変更は両方のテーブルに対して行われます。そのためには変更できるオブジェクトは1つでなければなりません。次の例ではこのポイントを説明するために StringBuf オブジェクトが変更可能であるという事実を使用します。StringBuf.insert メソッドを使って、ハッシュテーブルが指し示すデータ オブジェクトを変更します。この場合、変更は、オリジナルのテーブルとそのクローンの両方に影響します。

例: ハッシュ テーブルの要素の変更
{let my-hash-table:{HashTable-of String, StringBuf} =
{new {HashTable-of String, StringBuf},
"home", {StringBuf "781-648-4031"},
"cell", {StringBuf "781-956-1033"},
"fax", {StringBuf "781-648-4032"}
}
}
{let new-hash-table:{HashTable-of String, StringBuf} = 
{my-hash-table.clone}
}
{let table1:Table = {Table columns = 2}}
{let table2:Table = {Table columns = 2}}
{for e:StringBuf key k:String in new-hash-table do
{e.insert '1', 0}
{e.insert '-', 1}
}
{for e:StringBuf key k:String in my-hash-table do
{table1.add k}
{table1.add e}
}
{for e:StringBuf key k:String in new-hash-table do
{table2.add k}
{table2.add e}
}
{HBox
"Original:",
{Fill width = .5cm},
table1,
{Fill width = .5cm},
"Clone:",
{Fill width = .5cm},
table2
}

HashTable-of.filter-clone は、要素の値をフィルタするプロシージャによって返された要素を含むクローンを作成します。次の例では、値が 50 を超える要素をフィルタします。

例: 要素のクローン作成とフィルタ
{value
    let my-hash-table:{HashTable-of String, int} =
        {new {HashTable-of String, int},
            "Apple", 56,
            "Banana", 87,
            "Cherry", 34
        }
    let new-hash-table:{HashTable-of String, int} =
        {my-hash-table.filter-clone
            {proc {element:int}:bool
                {return element > 50}
            }
        }
    let box1:VBox = {VBox}
    let box2:VBox = {VBox}
    {for e:int key k:String in my-hash-table do
        {box1.add 
            {HBox spacing = 5pt, e, k}
        }
    }
    {for e:int key k:String in new-hash-table do
        {box2.add 
            {HBox spacing = 5pt, e, k}
        }
    }
    {HBox
        box1,
        {Fill width = .5cm},
        box2
    } 
}
HashTable-of.filter-keys-clone は、キーの値をフィルタするプロシージャによって返された要素を含むクローンを作成します。次の例では文字 'B' で始まるキーをフィルタして取り除きます。

例: キーのクローン作成とフィルタ
{value
let my-hash-table:{HashTable-of String, int} =
    {new {HashTable-of String, int},
        "Apple", 56,
        "Banana", 87,
        "Cherry", 34
    }
let new-hash-table:{HashTable-of String, int} =
    {my-hash-table.filter-keys-clone
        {proc {key:String}:bool
            {return key[0] != 'B'}
        }
    }
let box1:VBox = {VBox}
let box2:VBox = {VBox}
{for e:int key k:String in my-hash-table do
    {box1.add 
        {HBox spacing = 5pt, e, k}
    }
}
{for e:int key k:String in new-hash-table do
    {box2.add 
        {HBox spacing = 5pt, e, k}
    }
}
{HBox
    box1,
    {Fill width = .5cm},
    box2
}
}

ハッシュ テーブルの管理

次のリストは、ハッシュ テーブルの管理に使用することができる追加のメソッドです。