1. 简介

在 Kotlin 中,枚举(Enum) 是一种强大的语言特性,允许我们定义一组命名常量。它常用于表示封闭的值集合,比如一周中的每一天或一年中的各月份。实际开发中,我们经常会遇到一个需求:判断某个字符串是否对应某个 enum 的值

本文将介绍几种在 Kotlin 中判断 enum 是否包含指定字符串的方法,帮助你在不同场景下选择最优解,避免踩坑 ❌。

2. Enum 类定义

为了统一演示,我们先定义一个表示“星期”的枚举类:

enum class DaysOfWeek {
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY

    companion object {
        val names by lazy { DaysOfWeek.values().map { it.name } }
    }
}

这个 DaysOfWeek 枚举包含了七天。同时,在其伴生对象(companion object)中,我们通过 lazy 延迟初始化了一个 names 列表,保存所有枚举项的名称(即 name 字段)。这能提升性能,尤其在频繁调用但初始化成本较高的场景下 ✅。

3. 使用 for 循环

最直观的方式是遍历所有枚举值,逐一比对名称:

fun containsValueUsingForLoop(value: String): Boolean {
    for (day in DaysOfWeek.values()) {
        if (day.name == value) {
            return true
        }
    }
    return false
}

优点:逻辑清晰,兼容性好,适合初学者理解。
缺点:手动写循环略显冗余,不够函数式。

单元测试验证

@Test
fun `checks if enum value contains a specific string correctly using for loop`() {
    assertFalse(containsValueUsingForLoop("RED"))
    assertTrue(containsValueUsingForLoop("WEDNESDAY"))
    assertFalse(containsValueUsingForLoop("AUGUST"))
    assertTrue(containsValueUsingForLoop("SATURDAY"))
}

测试覆盖了命中与未命中的情况,确保逻辑正确。

4. 使用 when 表达式

Kotlin 的 when 不仅是 switch 的升级版,还能结合 in 操作符实现成员判断:

fun containsValueUsingWhenExpression(value: String): Boolean {
    return when (value) {
        in DaysOfWeek.names -> true
        else -> false
    }
}

⚠️ 注意:这里依赖了前面定义的 names 属性,本质上是判断字符串是否在名称列表中。

优点:语法简洁,语义明确,无需显式循环。
缺点:仍需维护 names 列表,若忘记更新可能出错。

测试代码

@Test
fun `checks if enum value contains a specific string correctly using when expression`() {
    assertFalse(containsValueUsingWhenExpression("RED"))
    assertTrue(containsValueUsingWhenExpression("WEDNESDAY"))
    assertFalse(containsValueUsingWhenExpression("AUGUST"))
    assertTrue(containsValueUsingWhenExpression("SATURDAY"))
}

结果符合预期。

5. 使用 map 方法

我们可以利用 map 提取所有枚举名称,生成一个字符串列表,再使用 contains 判断:

@Test
fun `checks if enum value contains a specific string correctly using map method`() {
    val names = DaysOfWeek.values().map { it.name }

    assertFalse(names.contains("RED"))
    assertTrue(names.contains("WEDNESDAY"))
    assertFalse(names.contains("AUGUST"))
    assertTrue(names.contains("SATURDAY"))
}

⚠️ 虽然可行,但这种方式会完整遍历所有枚举值并创建新列表,即使第一个就匹配成功也不会提前退出 —— 存在性能浪费。

5.1 使用 any 方法(推荐优化)

更高效的做法是使用 any,它支持短路求值(short-circuiting),一旦找到匹配项就立即返回 true

@Test
fun `checks if enum value contains a specific string correctly using any method`() {
    val values = DaysOfWeek.values()
    
    assertFalse(values.any { it.name == "RED" })
    assertTrue(values.any { it.name == "WEDNESDAY" })
    assertFalse(values.any { it.name == "AUGUST" })
    assertTrue(values.any { it.name == "SATURDAY" })
}

核心优势

  • 不需要预先构建列表
  • 遇到第一个匹配项即停止迭代
  • 内存友好,时间复杂度更优(平均情况下)

📌 实际项目中建议优先使用 any 替代 map + contains,特别是在枚举项较多时性能差异明显。

6. 使用 HashSet

另一种思路是将所有枚举名称预加载进 HashSet,利用哈希表 O(1) 查找特性提升查询效率:

fun containsValueUsingHashSet(value: String): Boolean {
    val set = HashSet<String>()
    DaysOfWeek.values().forEach { set.add(it.name) }
    return set.contains(value)
}

不过上面写法每次调用都重建 HashSet,显然不合理 ❌。生产环境应改为静态缓存:

enum class DaysOfWeek {
    // ... 其他枚举值
    ;

    companion object {
        private val nameSet by lazy {
            hashSetOf<String>().apply {
                values().forEach { add(it.name) }
            }
        }

        fun containsName(name: String): Boolean = nameSet.contains(name)
    }
}

适用场景:高频查询、低频变更的枚举(如配置类型、状态码等)
⚠️ 注意点:务必配合 lazystatic 缓存,否则得不偿失。

测试验证

@Test
fun `checks if enum value contains a specific string correctly using a hashset`() {
    assertFalse(containsValueUsingHashSet("RED"))
    assertTrue(containsValueUsingHashSet("WEDNESDAY"))
    assertFalse(containsValueUsingHashSet("AUGUST"))
    assertTrue(containsValueUsingHashSet("SATURDAY"))
}

7. 总结

方法 是否推荐 说明
for 循环 ⚠️ 一般 易懂但不够优雅
when + in names ✅ 可用 依赖辅助属性,注意一致性
map { name } + contains ❌ 不推荐 全量映射,无法短路
any { name == value } ✅✅ 强烈推荐 短路求值,零额外内存开销
HashSet 缓存 ✅ 高频查询场景 查询快,适合常驻内存

📌 最佳实践建议

  • 日常小枚举 ➜ 直接用 any
  • 大枚举或高频查询 ➜ 配合 HashSet + lazy 缓存
  • 避免重复造轮子,善用标准库函数

掌握这些技巧不仅能写出更高效的代码,也能加深对 Kotlin 函数式编程特性的理解。下次遇到类似需求时,就知道该怎么选了 💡。


原始标题:Check if an Enum Value Contains a Given String in Kotlin