Rust中如何按多类别向量的单个类别分组Document集合?
在Rust中按多类别分组Document集合
这其实是个典型的多键分组场景,用Rust标准库的HashMap就能轻松搞定,我给你两种实现思路,从直观到优化一步步来:
基础实现(适合小文档场景)
首先,因为每个文档可能属于多个类别,我们需要遍历每个文档,再遍历它的所有类别,把文档添加到对应类别的列表里。这里要注意所有权的问题,所以我们给Document派生Clone trait来支持克隆:
use std::collections::HashMap; #[derive(Debug, Clone)] struct Document { categories: Vec<String>, body: String, } fn main() { let docs = vec![ Document { categories: vec!["rust".to_string()], body: "doc1".to_string() }, Document { categories: vec!["clojure".to_string()], body: "doc2".to_string() }, Document { categories: vec!["java".to_string()], body: "doc3".to_string() }, Document { categories: vec!["rust".to_string(), "clojure".to_string()], body: "doc4".to_string() }, ]; // 初始化分组用的HashMap let mut category_map: HashMap<String, Vec<Document>> = HashMap::new(); for doc in docs { // 遍历当前文档的每个类别 for category in doc.categories.clone() { // 用entry API处理"不存在则创建,存在则追加"的逻辑 category_map.entry(category) .or_insert_with(Vec::new) .push(doc.clone()); } } // 打印验证结果 for (category, docs) in category_map { println!("{:?} => {:?}", category, docs); } }
关键点说明:
Clone派生:因为像doc4这样的文档需要同时出现在两个类别列表里,所以我们需要克隆文档的副本。entryAPI:这是Rust处理HashMap更新的惯用手法,比先判断键是否存在再插入更高效简洁。- 类别克隆:我们克隆了
category字符串,因为原类别属于文档的字段,直接转移所有权会导致后续遍历其他类别时出错,克隆是最直观的解决方式。
优化实现(适合大文档场景)
如果你的文档体积很大,克隆整个文档的成本太高,可以用Rc(引用计数指针)来共享文档的所有权,这样每个类别列表里只存指向原文档的指针,不需要克隆整个文档:
use std::collections::HashMap; use std::rc::Rc; #[derive(Debug)] struct Document { categories: Vec<String>, body: String, } fn main() { let docs = vec![ Rc::new(Document { categories: vec!["rust".to_string()], body: "doc1".to_string() }), Rc::new(Document { categories: vec!["clojure".to_string()], body: "doc2".to_string() }), Rc::new(Document { categories: vec!["java".to_string()], body: "doc3".to_string() }), Rc::new(Document { categories: vec!["rust".to_string(), "clojure".to_string()], body: "doc4".to_string() }), ]; let mut category_map: HashMap<String, Vec<Rc<Document>>> = HashMap::new(); for doc in docs { for category in doc.categories.clone() { // 克隆Rc只是增加引用计数,几乎没有成本 category_map.entry(category) .or_insert_with(Vec::new) .push(Rc::clone(&doc)); } } for (category, docs) in category_map { println!("{:?} => {:?}", category, docs); } }
优化点说明:
Rc的作用:通过引用计数共享所有权,多个类别列表可以指向同一个文档实例,避免了大对象的克隆开销。- 不需要
Clone派生:因为我们不再克隆Document本身,只需要克隆Rc指针即可。
运行这两段代码,都能得到你预期的分组结果:
"rust" => [Document { categories: ["rust"], body: "doc1" }, Document { categories: ["rust", "clojure"], body: "doc4" }] "clojure" => [Document { categories: ["clojure"], body: "doc2" }, Document { categories: ["rust", "clojure"], body: "doc4" }] "java" => [Document { categories: ["java"], body: "doc3" }]
内容的提问来源于stack exchange,提问作者Linca
相关产品推荐
相关产品推荐

