在開發程式時,我們常會使用HashMap來儲存key-value結構的資料。但很多時候我們的key-value資料會有不只一層,例如一個男女分班的學校,我們就需要有性別、班級、學號這三層的key-value結構,以確保各層的資料不會有重複的情況發生。或是可以分成國家、一級行政區、二級行政區等多層資料的世界地圖。



Leveled HashMap

「Leveled HashMap」是筆者開發的套件,提供了LeveledHashMap結構體,用來處理階層式的key-value資料,LeveledHashMap結構體的每一層都是一個HashMap結構實體,並非是每個key-value條目底下都自帶一個HashMap結構實體,因此可以確保每層的key皆不會相同。如此一來,在搜尋時可以直接指定要從哪個層級開始搜尋,效率極高。

Crates.io

Cargo.toml

leveled-hash-map = "*"

使用方法

使用use關鍵字將leveled_hash_map這個crate下的LeveledHashMap結構體給引用到當前的程式範圍下。LeveledHashMap結構體的new關聯函數可以建立出LeveledHashMap結構體的實體。

use leveled_hash_map::LeveledHashMap;

let map: LeveledHashMap<&'static str, String> = LeveledHashMap::new();

以下介紹LeveledHashMap結構實體的增、刪、查、改之方式。

新增

LeveledHashMap結構實體提供了insert方法,可以將一個值,新增到一個Key鏈下。例如:

use std::sync::Arc;

use leveled_hash_map::LeveledHashMap;

let mut map: LeveledHashMap<&'static str, String> = LeveledHashMap::new();

map.insert(&[Arc::new("food")], "食物".to_string()).unwrap();
map.insert(&[Arc::new("food"), Arc::new("dessert")], "甜點".to_string()).unwrap();

如果該Key鏈已經有存在的值,就會被回傳出來。

如果想要在同一層下,新增多個值,可以使用LeveledHashMap結構實體提供的insert_many方法,直接將一個HashMap結構實體新增至LeveledHashMap結構實體中的某個Key鏈的子Key中。例如:

use std::sync::Arc;
use std::collections::HashMap;

use leveled_hash_map::LeveledHashMap;

let mut map: LeveledHashMap<&'static str, String> = LeveledHashMap::new();

map.insert(&[Arc::new("food")], "食物".to_string()).unwrap();

let mut insert_map = HashMap::new();

insert_map.insert("dessert", "甜點".to_string());
insert_map.insert("meat", "肉類".to_string());

map.insert_many(&[Arc::new("food")], insert_map, 0).unwrap();

如果該Key鏈的子Key中已經有存在的值,就會被回傳出來。

刪除

LeveledHashMap結構實體提供了remove方法,可以將一個值從一個Key鏈上移除。例如:

use std::sync::Arc;

use leveled_hash_map::LeveledHashMap;

let mut map: LeveledHashMap<&'static str, String> = LeveledHashMap::new();

map.insert(&[Arc::new("food")], "食物".to_string()).unwrap();

map.insert(&[Arc::new("food"), Arc::new("dessert")], "甜點".to_string()).unwrap();

map.insert(&[Arc::new("food"), Arc::new("meat")], "肉類".to_string()).unwrap();

let result = map.remove(&[Arc::new("food"), Arc::new("dessert")]).unwrap();

assert_eq!("甜點", result.0);
assert_eq!(0, result.1.len());

let result = map.remove(&[Arc::new("food")]).unwrap();

assert_eq!("食物", result.0);
assert_eq!(1, result.1.len());
assert_eq!(&(Some(Arc::new("food")), "肉類".to_string()), result.1[0].get(&Arc::new("meat")).unwrap());

如果該Key鏈上確實有值,就會將該值和該Key鏈下所有層級的子Key和值回傳出來。

也可以使用LeveledHashMap結構實體提供的remove_advanced方法,來直接從某個層級開始,移除指定的Key鏈。可以省下檢查前面幾層Key鏈正確性的時間。例如:

use std::sync::Arc;

use leveled_hash_map::LeveledHashMap;

let mut map: LeveledHashMap<&'static str, String> = LeveledHashMap::new();

map.insert(&[Arc::new("food")], "食物".to_string()).unwrap();

map.insert(&[Arc::new("food"), Arc::new("dessert")], "甜點".to_string()).unwrap();

map.insert(&[Arc::new("food"), Arc::new("meat")], "肉類".to_string()).unwrap();

let result = map.remove_advanced(&[Arc::new("dessert")], 1).unwrap();

assert_eq!("甜點", result.0);
assert_eq!(0, result.1.len());

let result = map.remove_advanced(&[Arc::new("food")], 0).unwrap();

assert_eq!("食物", result.0);
assert_eq!(1, result.1.len());
assert_eq!(&(Some(Arc::new("food")), "肉類".to_string()), result.1[0].get(&Arc::new("meat")).unwrap());
查詢

LeveledHashMap結構實體提供了get方法,可以取得一個Key鏈上之值的不可變參考。例如:

use std::sync::Arc;

use leveled_hash_map::LeveledHashMap;

let mut map: LeveledHashMap<&'static str, String> = LeveledHashMap::new();

map.insert(&[Arc::new("food")], "食物".to_string()).unwrap();

map.insert(&[Arc::new("food"), Arc::new("dessert")], "甜點".to_string()).unwrap();

let desert = map.get(&[Arc::new("food"), Arc::new("dessert")]);

也可以使用LeveledHashMap結構實體提供的get_advanced方法,來直接從某個層級開始,取得指定Key鏈的值的不可變參考。可以省下檢查前面幾層Key鏈正確性的時間。例如:

use std::sync::Arc;

use leveled_hash_map::LeveledHashMap;

let mut map: LeveledHashMap<&'static str, String> = LeveledHashMap::new();

map.insert(&[Arc::new("food")], "食物".to_string()).unwrap();

map.insert(&[Arc::new("food"), Arc::new("dessert")], "甜點".to_string()).unwrap();

let desert = map.get_advanced(&[Arc::new("dessert")], 1);

如果要取得某指定層級的HashMap結構實體的(不可變)參考,可以使用LeveledHashMap結構實體提供的keys方法。例如:

use std::sync::Arc;
use std::collections::HashMap;

use leveled_hash_map::LeveledHashMap;

let mut map: LeveledHashMap<&'static str, String> = LeveledHashMap::new();

map.insert(&[Arc::new("food")], "食物".to_string()).unwrap();

let mut insert_map = HashMap::new();

insert_map.insert("dessert", "甜點".to_string());
insert_map.insert("meat", "肉類".to_string());

map.insert_many(&[Arc::new("food")], insert_map, 0).unwrap();

let result = map.keys(0).unwrap();

assert_eq!(1, result.len());

let result = map.keys(1).unwrap();

assert_eq!(2, result.len());
修改

LeveledHashMap結構實體提供的各類get方法,方法名稱後面皆可以加上_mut,來取得值的可變參考,如此就可以直接改變LeveledHashMap結構實體內的值。

使用案例

以下簡單建立一個擁有多國語言名稱的抽象「世界地圖」:

use std::sync::Arc;
use std::collections::HashMap;

use leveled_hash_map::LeveledHashMap;

#[derive(Debug)]
struct MultiName {
    us: &'static str,
    tw: &'static str,
    cn: &'static str,
}

fn main() {
    let earth_map: LeveledHashMap<&'static str, MultiName> = {
        let mut map = LeveledHashMap::new();

        map.insert(&[Arc::new("US")], MultiName {
            us: "United States of America",
            tw: "美國",
            cn: "美国",
        }).unwrap();

        let mut us_states = HashMap::new();

        us_states.insert("New York", MultiName {
            us: "New York",
            tw: "紐約州",
            cn: "纽约州",
        });
        us_states.insert("Utah", MultiName {
            us: "Utah",
            tw: "猶他州",
            cn: "犹他州",
        });

        map.insert_many(&[Arc::new("US")], us_states, 0).unwrap();

        map.insert(&[Arc::new("CN")], MultiName {
            us: "China",
            tw: "中國",
            cn: "中国",
        }).unwrap();

        let mut cn_provinces = HashMap::new();

        cn_provinces.insert("Guangdong", MultiName {
            us: "Guangdong",
            tw: "廣東省",
            cn: "广东省",
        });
        cn_provinces.insert("Fujian", MultiName {
            us: "Fujian",
            tw: "福建省",
            cn: "福建省",
        });

        map.insert_many(&[Arc::new("CN")], cn_provinces, 0).unwrap();

        map.insert(&[Arc::new("TW")], MultiName {
            us: "Taiwan",
            tw: "臺灣",
            cn: "臺湾",
        }).unwrap();

        let mut tw_counties = HashMap::new();

        tw_counties.insert("Taipei", MultiName {
            us: "Taipei",
            tw: "台北",
            cn: "台北",
        });

        tw_counties.insert("Taichung", MultiName {
            us: "Taichung",
            tw: "台中",
            cn: "台中",
        });

        map.insert_many(&[Arc::new("TW")], tw_counties, 0).unwrap();

        map
    };

    println!("{:?}", earth_map);

    let countries = earth_map.keys(0).unwrap();

    println!("{:?}", countries); // {"CN": {"Fujian", "Guangdong"}, "US": {"Utah", "New York"}, "TW": {"Taichung", "Taipei"}}

    let new_york = earth_map.get(&[Arc::new("US"), Arc::new("New York")]).unwrap();

    println!("{:?}", new_york); // MultiName { us: "New York", tw: "紐約州", cn: "纽约州" }

    let new_york = earth_map.get_advanced(&[Arc::new("New York")], 1).unwrap();

    println!("{:?}", new_york); // MultiName { us: "New York", tw: "紐約州", cn: "纽约州" }

    let new_york_suspicion = earth_map.get(&[Arc::new("TW"), Arc::new("New York")]);

    println!("{:?}", new_york_suspicion); // None
}