Rustについて基本からしっかり学んでいく本連載。第10回は、Rustのハッシュマップと文字列について。
この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。
第9回ではデータ型を抽象化するジェネリクスと、ジェネリクスを生かしたデータ型でコレクションの1つでもあるベクターを紹介しました。今回は、残るコレクションであるハッシュマップと文字列を紹介します。
ハッシュマップの具体的な利用方法を見ていきます。
ハッシュマップも、ベクターと同様にnew()メソッドを用いて宣言します。
use std::collections::HashMap; (1) …略… let mut months: HashMap<String, i32> = HashMap::new(); (2) let mut months12: HashMap<String, i32> = HashMap::with_capacity(12); (3)
(1)では、ハッシュマップの使用をuse文により宣言しています。このように、ハッシュマップのためのモジュールを有効にするuse文が必要なことに注意してください。これは、ベクターに比べてハッシュマップの使用頻度はそれほど高くないという判断に基づくようです。
(2)では、変数monthsをHashMap<String, i32>型のインスタンスを生成して所有させています。ここではキーにString型を指定していますが、ほかにboolean型、i32型などの整数型など、EqトレイトとHashトレイトを実装したデータ型をキーに指定することができます(トレイトについては次回以降で紹介します)。このように、ハッシュマップはHashMap型の構造体を生成して使うことになります。ここで、型注釈を使用して型を明示しているのに注意してください。HashMap型のnew()メソッドを呼び出しただけではハッシュマップの中身は空なので、キーと値の型を特定できないからです。型注釈を省略するには、次で紹介するinsert()メソッドなどでキーと値のデータ型を明確にするか、ベクターを使った生成を用います。
(3)では、with_capacity()メソッドを使用してハッシュマップを生成しています。with_capacity()メソッドは引数に整数値を受け取り、その大きさ分のメモリ領域をあらかじめ確保します。ハッシュマップへ値を追加していくなどしても、この数を超えない限りメモリ領域は再配置されません。あらかじめ、上限が分かっている(例のように12個が最大であるなど)場合には速度面で有利ですが、使われない部分は無駄になるので、バランスを考えて使用しましょう。なお0を指定した場合、ハッシュマップの生成時点ではメモリ領域は確保されず、最初の追加ではじめてメモリ領域が確保されます。
ハッシュマップには、ベクターにおけるvec!のようなマクロは用意されていません。その代わりというわけではないですが、ベクターからハッシュマップを生成することができます。これには、キーだけのベクター、値だけのベクターを作り、それをcollect()メソッドでまとめます。
let keys = vec!["January", "February", "March", "April"]; let values = vec![1, 2, 3, 4]; let months: HashMap<_, _> = keys.iter().zip(values.iter()).collect(); (1) println!("{:?}", months); // {"April": 4, "March": 3, "February": 2, "January": 1}
(1)に注目してください。keys.iter()は、keysに対するイテレータを返しますが、そのzip()メソッドがvalues.iter()と合わせて1つのイテレータにまとめてくれます。これにcollect()メソッドを適用することで、ハッシュマップに変換します。なお、monthsの初期化にはHashMap<_, _>型を明示していますが、ここに含まれるアンダースコア(_)により、ベクターの型から自動的にキーと値の型を決めてくれます。
ハッシュにおいても、文字列をキーや値に使用する場合にはベクターと同様の注意が必要です。第9回の補足「文字列をベクターに入れるとき」を参照してください。
Copyright © ITmedia, Inc. All Rights Reserved.