
本文介绍在 java/kotlin 中为小型、固定结构的 pojo 集合设计轻量级查询方案:摒弃冗余多索引映射,采用 specification 模式实现类型安全、可组合的运行时条件过滤,并兼顾可扩展性与持久化路径。
本文介绍在 java/kotlin 中为小型、固定结构的 pojo 集合设计轻量级查询方案:摒弃冗余多索引映射,采用 specification 模式实现类型安全、可组合的运行时条件过滤,并兼顾可扩展性与持久化路径。
在管理小型应用状态(如配置、会话元数据、UI 状态快照)时,数据规模通常仅几十至数百条,结构稳定、字段固定,且查询需求以单字段等值匹配为主(如 a == 5、b == "abc")。此时盲目引入数据库或构建多层哈希索引(如 Map
✅ 推荐方案:Specification 模式(Java/Kotlin 通用)
Specification 是一种行为型设计模式,将查询条件封装为可复用、可组合的对象。它天然契合函数式思维,在 Kotlin 中尤为简洁;在 Java 中亦可通过函数式接口(如 Predicate
以下为 Kotlin 实现(兼容 Java 17+,利用密封类与高阶函数提升类型安全):
interface Specification<T> {
fun isSatisfiedBy(obj: T): Boolean
}
// 基础原子条件:按属性值匹配
class FieldValue<T, V>(
private val extractor: (T) -> V,
private val target: V
) : Specification<T> {
override fun isSatisfiedBy(obj: T): Boolean = extractor(obj) == target
}
// 组合逻辑:AND / OR
class And<T>(private val left: Specification<T>, private val right: Specification<T>) : Specification<T> {
override fun isSatisfiedBy(obj: T): Boolean = left.isSatisfiedBy(obj) && right.isSatisfiedBy(obj)
}
class Or<T>(private val left: Specification<T>, private val right: Specification<T>) : Specification<T> {
override fun isSatisfiedBy(obj: T): Boolean = left.isSatisfiedBy(obj) || right.isSatisfiedBy(obj)
}
// 可选:NOT 支持(增强表达力)
class Not<T>(private val spec: Specification<T>) : Specification<T> {
override fun isSatisfiedBy(obj: T): Boolean = !spec.isSatisfiedBy(obj)
}使用示例(假设 MyObject 已定义为 data class MyObject(val a: Int, val b: String, val c: Boolean)):
val data = listOf(
MyObject(1, "abc", true),
MyObject(2, "def", false),
MyObject(3, "abc", false),
MyObject(4, "ghi", true),
MyObject(5, "abc", true)
)
// 构建复合查询:a == 5 或 (b == "abc" 且 c == true)
val query = Or(
FieldValue(MyObject::a, 5),
And(
FieldValue(MyObject::b, "abc"),
FieldValue(MyObject::c, true)
)
)
val results = data.filter(query::isSatisfiedBy) // 返回 [MyObject(5,"abc",true)]? 为什么比多 Map 更优?
| 维度 | 多 Map 方案 | Specification 方案 |
|---|---|---|
| 内存占用 | O(N×字段数),每字段独立存储副本 | O(N),仅存原始对象列表 |
| 写入性能 | 插入/删除需同步更新多个 Map | 直接操作 List,零额外开销 |
| 查询灵活性 | 仅支持单字段等值查询 | 支持 AND/OR/NOT 任意嵌套,未来可扩展范围查询、正则匹配等 |
| 类型安全 | Map |
编译期检查属性类型,无歧义 |
| 可读性 | aMap[5] ?: bMap["abc"] 逻辑分散 | Or(FieldValue(...), FieldValue(...)) 语义清晰 |
⚠️ 注意事项:
- 性能边界:当数据量持续增长至数千条以上,且高频执行复杂查询时,应考虑引入内存数据库(如 H2 Embedded)或升级为 Lucene 等倒排索引方案。
- Kotlin 特性利用:推荐使用 data class + copy() 支持不可变更新;配合 Sequence
可延迟计算,避免中间集合生成。 - Java 兼容写法:Java 11+ 可直接使用 Predicate
构建链式条件: Predicate<MyObject> p1 = obj -> obj.getA() == 5; Predicate<MyObject> p2 = obj -> "abc".equals(obj.getB()) && obj.isC(); List<MyObject> results = data.stream() .filter(p1.or(p2)) .toList();
? 持久化扩展建议(满足可选需求)
若需跨进程重启保留数据,Specification 模式仍保持优势:
-
序列化友好:List
可直接通过 Jackson/Gson 序列化为 JSON 文件; - 查询逻辑解耦:持久化层只负责存储原始数据,查询逻辑仍由 Specification 在内存中执行;
- 进阶选型:对一致性要求高时,选用嵌入式键值库(如 Jetbrains Exposed + H2),用 SELECT * FROM my_object WHERE a = ? 替代内存过滤,同时保留 Specification 的抽象能力(将 FieldValue 映射为 SQL WHERE 条件)。
综上,面对小型状态数据,放弃预建索引,拥抱运行时查询抽象,是平衡简洁性、可维护性与扩展性的务实之选。Specification 不仅解决当下问题,更为未来查询演进预留了清晰路径。










