Rust中的搜索:以最高性能查找数组元素的索引

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

引言:为什么在Rust中查找索引不仅仅是“找到并返回”?

\n\n

每一位从Python或JavaScript转向Rust的开发者,迟早都会问这样一个问题:“在Rust中,如何找到数组或向量中元素的索引?”在Python中,只需一行代码list.index(element),在JavaScript中则是array.indexOf(element)。但Rust,作为一个真正的完美主义者,提供了不止一种,而是多种方法,每种方法在性能和安全性方面都有其细微差别。

\n\n

让我们来弄清楚如何在Rust集合(数组、向量和切片)中查找元素索引,使代码不仅正确,而且快速。毕竟,在系统编程中,每一纳秒都至关重要!

\n\n

基础:Rust中查找索引的主要方法

\n\n

在Rust标准库中,有两个主要的“参与者”用于查找索引:position()和rposition()。它们可用于任何迭代器,因此也适用于数组、向量和切片。

\n\n

position()方法:从左到右搜索

\n\n

position()接受一个闭包,并返回Option<usize>——第一个满足条件的元素的索引,如果未找到这样的元素,则返回None。

\n\n
fn main() {\n    let numbers = vec![10, 20, 30, 40, 50];\n    \n    // 查找数字30的第一次出现\n    let index = numbers.iter().position(|&x| x == 30);\n    \n    match index {\n        Some(i) => println!("数字30的索引: {}", i), // 输出: 2\n        None => println!("未找到元素"),\n    }\n}\n
\n\n

请注意:我们在position()之前调用了iter()。这很重要,因为position()是Iterator trait的方法,而不是集合本身的方法。

\n\n

rposition()方法:从右到左搜索

\n\n

如果您需要元素的最后一次出现的索引,请使用rposition()。它的工作方式类似,但从集合的末尾开始遍历:

\n\n
fn main() {\n    let data = vec!['a', 'b', 'c', 'b', 'a'];\n    \n    // 查找'b'的最后一次出现\n    let last_b = data.iter().rposition(|&c| c == 'b');\n    \n    println!("最后一个'b'的索引: {:?}", last_b); // Some(3)\n}\n
\n\n

一个重要限制:rposition()仅适用于支持双向遍历的集合(DoubleEndedIterator trait)。幸运的是,Rust的所有标准集合都满足此要求。

\n\n

性能比较:position() vs 手动遍历

\n\n

许多初学者会手动编写循环,认为这样更快。让我们在实践中验证这个假设。

\n\n

考虑两种在包含100万个整数的向量中查找元素索引的方法:

\n\n
use std::time::Instant;\n\nfn main() {\n    let large_vec: Vec<i32> = (0..1_000_000).collect();\n    let target = 999_999;\n    \n    // 方法1:position()\n    let start = Instant::now();\n    let pos1 = large_vec.iter().position(|&x| x == target);\n    let duration1 = start.elapsed();\n    println!("position(): {:?}, 耗时: {:?}", pos1, duration1);\n    \n    // 方法2:手动循环\n    let start = Instant::now();\n    let mut pos2 = None;\n    for (i, &val) in large_vec.iter().enumerate() {\n        if val == target {\n            pos2 = Some(i);\n            break;\n        }\n    }\n    let duration2 = start.elapsed();\n    println!("手动循环: {:?}, 耗时: {:?}", pos2, duration2);\n}\n
\n\n

在现代处理器上的结果通常表明,position()的运行速度与手动循环一样快,有时甚至更快,这得益于Rust编译器(LLVM)的内部优化。结论:使用position()——它符合语言习惯、安全且速度不逊色。

\n\n

二分查找:当数据已排序时

\n\n

如果您的集合已排序,您应该使用binary_se

博客

书籍推荐