Swift で自作オブジェクトを Dictionary のキーで使う

Swift プログラミング

Swift の Dictionary のキーとして自作オブジェクトを使うには、オブジェクトを Hashable プロトコルに準拠させる必要があります。

自作オブジェクトをDictionary のキーにするには

Swift言語Dictionary型 では、Hashableプロトコル に準拠したオブジェクトをキーとして使用できます。

たとえば、次のようなクラスがあったとします。

class KeyClass {
	
	var value:String
	
	init(_ value:String) {
		
		self.value = value
	}
}

このKeyClassクラスDictionary型 のキーとして使えるようにしてみます。

Hashableプロトコル

Hashableプロトコル に準拠するには、次の機能を実装する必要があります。

protocol Hashable : Equatable {

    var hashValue: Int { get }
}

このhashValueプロパティ は、これを実装したオブジェクトの値から一意に定まる値をInt型 で返すプロパティです。

オブジェクトが「同じ値」を持っているときには必ず、同じハッシュ値を返す必要があります。


重要なのは「同じ値のときには同じハッシュ値になる」ことで、異なるオブジェクトで同じ値になっても問題ありません。

ハッシュ値が異なる場合は、値が異なることが確実なので、別の値として扱われます。ハッシュ値が同じ場合は、それが本当に同じ値かどうか、等価判定が行われます。

Dictionary では、キーをハッシュテーブルで管理して、値の書き込みや読み出しの効率化が図られているようです。

Equatableプロトコル

値が異なるオブジェクトから同じハッシュ値が得られる可能性があるため、本当に同じオブジェクトであるかを判定できるようにHashableプロトコル に準拠する必要があります。

protocol Equatable {

    func ==(lhs: Self, rhs: Self) -> Bool
}

ここで定義された等価演算子を実装することで、オブジェクトの値が一致していることを判定できるようになります。

Dictionary のキーにできる自作クラスを作成する

オブジェクトに上記の機能を実装することで、オブジェクトをDictionary のキーとして使えるようになります。

extension KeyClass : Hashable {
	
	var hashValue: Int {
		
		return self.value.hashValue
	}
}

func == (lhs:KeyClass, rhs:KeyClass) -> Bool {
	
	return lhs.value == rhs.value
}

自作オブジェクトをDictionary のキーとして使う

上記のようにオブジェクトを定義できたら、あとは普段通りにオブジェクトをキーに指定できます。

var dic = [KeyClass:Int]()

let k1 = KeyClass("A")
let k2 = KeyClass("B")
let k3 = KeyClass("C")

dic[k1] = 1
dic[k2] = 2
dic[k3] = 3

Dictionary に値を設定するときの動き

このようにして、自作オブジェクトをキーにしてDictionary を使用すると、基本的な動きとしては、キーのハッシュ値を使用して、値が格納されている場所が特定されます。

このようなハッシュ値と格納値とを関連付けて管理する仕組みのことを、ハッシュテーブル と呼ぶようです。


値を書き込むときであれば、指定されたハッシュ値に対応する格納値がまだ存在しない場合は、新しい値として渡された値を格納します。読み込むときは、指定されたハッシュ値があればそれに対応する値を、そうでなければ nil を返すという動きになります。

ただ、ハッシュ値は異なるオブジェクトで同じ値になることがあるので、指定されたハッシュ値が既に格納されている場合は、渡されたキーと既存のキーとが同じものか、等価演算子を使って判定されます。渡された値は、それによって既存のキーと同じであればそれに対応する値として、異なれば新しいキーの値として、扱われることになります。

実際にはさらに、ハッシュテーブルの広さには限りがあるため、全てのハッシュ値をそれぞれ独立した場所に保存することができません。そのため、異なるハッシュ値であっても、同じ場所に値を格納しようとする場合があります。このときにも、キーが既に存在するかを判定するために等価演算子が使われます。