# 筛选与变换:提取已启用用户的邮箱
学习目标:对比函数式链式转换(Filter & Map)与命令式循环收集在各语言中的性能习惯与语法风格。
前置知识与范围:结构体/对象、列表与匿名函数/闭包。演示在内存记录序列上的过滤与字段投影。
# 问题与边界
给定一组用户记录,提取所有**处于激活状态(active == true)**的用户的邮箱地址列表:
- 输入列表:
- Alice(
email: "alice@example.com",active: true) - Bob(
email: "bob@example.com",active: false) - Charlie(
email: "charlie@example.com",active: true)
- Alice(
- 输出契约:顺序保留为
["alice@example.com", "charlie@example.com"];未激活的 Bob 必须被排除;若全部未激活或输入为空列表,返回空列表[]。
# 关键差异
| 语言 | 转换惯用风格 | 语法形态 | 中间临时集合分配 |
|---|---|---|---|
| Rust | 惰性迭代器链式消费 | .iter().filter(...).map(...).collect() |
零中间分配,终结操作一次性收集 |
| Go | 显式命令式 for 循环 |
手写 if u.Active { res = append(...) } |
无多余流抽象,性能可控直观 |
| Java | Stream API | .stream().filter(...).map(...).toList() |
惰性求值流管道 |
| JavaScript | 原生数组链式方法 | users.filter(...).map(...) |
产生中间过滤数组 |
| TypeScript | 沿用 JS 链式转换 | users.filter(...).map(...) |
静态字段类型推导保证 |
| Kotlin | 扩展函数链式调用 | users.filter { it.active }.map { it.email } |
默认及早求值产生中间列表 |
| Python | 列表推导式(List Comprehension) | [u.email for u in users if u.active] |
单层字节码直接高效生成结果 |
| C++ | 范围循环追加或算法库 | 范围 for 配合 res.push_back |
预留容量(reserve)消除重分配 |
# 各语言实现
# Rust
Ruststruct User<'a> {
name: &'a str,
email: &'a str,
active: bool,
}
fn extract_active_emails<'a>(users: &[User<'a>]) -> Vec<&'a str> {
users
.iter()
.filter(|u| u.active)
.map(|u| u.email)
.collect()
}
fn main() {
let users = [
User { name: "Alice", email: "alice@example.com", active: true },
User { name: "Bob", email: "bob@example.com", active: false },
User { name: "Charlie", email: "charlie@example.com", active: true },
];
let emails = extract_active_emails(&users);
assert_eq!(emails, vec!["alice@example.com", "charlie@example.com"]);
assert_eq!(extract_active_emails(&[]), Vec::<&str>::new());
println!("active: {}", emails.len());
}
Rust 的迭代器适配器是完全惰性的,只有在调用 collect() 时才执行计算。
# Go
Gopackage main
import "fmt"
type User struct {
Name string
Email string
Active bool
}
func ExtractActiveEmails(users []User) []string {
var emails []string
for _, u := range users {
if u.Active {
emails = append(emails, u.Email)
}
}
return emails
}
func main() {
users := []User{
{Name: "Alice", Email: "alice@example.com", Active: true},
{Name: "Bob", Email: "bob@example.com", Active: false},
{Name: "Charlie", Email: "charlie@example.com", Active: true},
}
emails := ExtractActiveEmails(users)
if len(emails) != 2 || emails[0] != "alice@example.com" || emails[1] != "charlie@example.com" {
panic("mismatch")
}
fmt.Printf("active: %d\n", len(emails))
}
Go 社区崇尚简单与透明,虽然可以通过反射或第三方库编写通用流,但显式循环是整个标准库与生态的标准写法。
# Java
Javaimport java.util.List;
public class Main {
public record User(String name, String email, boolean active) {}
public static List<String> extractActiveEmails(List<User> users) {
return users.stream()
.filter(User::active)
.map(User::email)
.toList();
}
public static void main(String[] args) {
List<User> users = List.of(
new User("Alice", "alice@example.com", true),
new User("Bob", "bob@example.com", false),
new User("Charlie", "charlie@example.com", true)
);
List<String> emails = extractActiveEmails(users);
assert emails.size() == 2;
assert "alice@example.com".equals(emails.get(0));
assert "charlie@example.com".equals(emails.get(1));
System.out.println("active: " + emails.size());
}
}
Java 16 的 Stream.toList() 返回不可修改的高性能列表视图。
# JavaScript
JavaScriptconst assert = {
equal: (a, b) => { if (a !== b) throw new Error(`${a} !== ${b}`); },
deepEqual: (a, b) => { if (JSON.stringify(a) !== JSON.stringify(b)) throw new Error(`${JSON.stringify(a)} !== ${JSON.stringify(b)}`); },
throws: (fn, errType) => {
try { fn(); throw new Error("expected throw"); } catch (e) {
if (e.message === "expected throw") throw e;
if (errType && !(e instanceof errType)) throw e;
}
}
};
function extractActiveEmails(users) {
return users
.filter(u => u.active)
.map(u => u.email);
}
const users = [
{ name: "Alice", email: "alice@example.com", active: true },
{ name: "Bob", email: "bob@example.com", active: false },
{ name: "Charlie", email: "charlie@example.com", active: true },
];
const emails = extractActiveEmails(users);
assert.deepEqual(emails, ["alice@example.com", "charlie@example.com"]);
console.log(`active: ${emails.length}`);
JavaScript 原生数组方法语义高度直观,但需要注意链式调用会生成中间临时数组。
# TypeScript
TypeScriptconst assert = {
equal: (a: unknown, b: unknown) => { if (a !== b) throw new Error(`${a} !== ${b}`); },
deepEqual: (a: unknown, b: unknown) => { if (JSON.stringify(a) !== JSON.stringify(b)) throw new Error(`${JSON.stringify(a)} !== ${JSON.stringify(b)}`); },
throws: (fn: () => void, errType?: unknown) => {
try { fn(); throw new Error("expected throw"); } catch (e: unknown) {
if (e instanceof Error && e.message === "expected throw") throw e;
if (typeof errType === "function" && !(e instanceof errType)) throw e;
}
}
};
interface User {
readonly name: string;
readonly email: string;
readonly active: boolean;
}
function extractActiveEmails(users: readonly User[]): string[] {
return users
.filter(u => u.active)
.map(u => u.email);
}
const users: readonly User[] = [
{ name: "Alice", email: "alice@example.com", active: true },
{ name: "Bob", email: "bob@example.com", active: false },
{ name: "Charlie", email: "charlie@example.com", active: true },
];
const emails = extractActiveEmails(users);
assert.deepEqual(emails, ["alice@example.com", "charlie@example.com"]);
console.log(`active: ${emails.length}`);
TypeScript 在管道每一步均推断出准确的上下文类型。
# Kotlin
Kotlindata class User(val name: String, val email: String, val active: Boolean)
fun extractActiveEmails(users: List<User>): List<String> {
return users.filter { it.active }.map { it.email }
}
fun main() {
val users = listOf(
User("Alice", "alice@example.com", true),
User("Bob", "bob@example.com", false),
User("Charlie", "charlie@example.com", true)
)
val emails = extractActiveEmails(users)
check(emails == listOf("alice@example.com", "charlie@example.com"))
println("active: ${emails.size}")
}
Kotlin 的高阶集合函数提供简洁的内部迭代器语法。若数据量极大可使用 .asSequence() 开启惰性计算。
# Python
Pythonfrom typing import NamedTuple, List
class User(NamedTuple):
name: str
email: str
active: bool
def extract_active_emails(users: List[User]) -> List[str]:
return [u.email for u in users if u.active]
if __name__ == "__main__":
users = [
User("Alice", "alice@example.com", True),
User("Bob", "bob@example.com", False),
User("Charlie", "charlie@example.com", True),
]
emails = extract_active_emails(users)
assert emails == ["alice@example.com", "charlie@example.com"]
print(f"active: {len(emails)}")
Python 社区优先推荐列表推导式,相比 filter() 配合 map() 可读性更好且执行更快。
# C++
C++#include <iostream>
#include <vector>
#include <string>
#include <cassert>
struct User {
std::string name;
std::string email;
bool active;
};
std::vector<std::string> extract_active_emails(const std::vector<User>& users) {
std::vector<std::string> emails;
for (const auto& u : users) {
if (u.active) {
emails.push_back(u.email);
}
}
return emails;
}
int main() {
std::vector<User> users = {
{"Alice", "alice@example.com", true},
{"Bob", "bob@example.com", false},
{"Charlie", "charlie@example.com", true}
};
auto emails = extract_active_emails(users);
assert(emails.size() == 2);
assert(emails[0] == "alice@example.com");
assert(emails[1] == "charlie@example.com");
std::cout << "active: " << emails.size() << "\n";
}
在 C++17 中,标准范围循环最为直白高效,避免了构建过度复杂的迭代器适配器。
# 迁移误区与适用限制
- 链式调用的内存开销:在 JS 或 Kotlin 中对十万级大数组执行
arr.filter(...).map(...),会在内存中分配完整的中间数组;而 Rust 迭代器或 Java Stream 则是按需流水线计算,不产生中间集合。 - 映射函数中的副作用:
map的初衷是纯函数转换;在映射过程中修改外部状态或执行网络请求容易引发难以排查的顺序问题。
# 运行与验证
语言版本基线见示例基线。 所有程序运行后控制台输出均为:
纯文本active: 2
筛选条件与提取字段映射断言均已通过。