Map常见使用方法

93 阅读9分钟

Scala 中 Map 常见使用方法(含核心 API、场景示例)

Scala 中的 Map 是键值对(Key-Value)  映射数据结构,核心特点与 Java 类似:Key 唯一(重复覆盖)、Value 可重复,但 Scala 区分「不可变 Map」(默认)和「可变 Map」,API 更简洁灵活,支持函数式编程风格。以下覆盖日常开发核心场景,基于 Scala 2.13+ 版本。

一、先明确:Scala Map 核心区别

类型特性初始化方式适用场景
不可变 Map(默认)初始化后无法修改(增删改)Map(...) 或 Map.empty无修改需求(线程安全、高效)
可变 Map支持动态增删改mutable.Map(...)需要频繁修改键值对的场景
SortedMap按 Key 自然排序 / 自定义排序SortedMap(...)需要按 Key 排序的场景
LinkedHashMap保留插入顺序mutable.LinkedHashMap(...)需要按插入顺序遍历的场景

注意:Scala 标准库中,Map 默认是不可变的(scala.collection.immutable.Map),可变 Map 需要显式导入 scala.collection.mutable.Map

二、核心基础操作(不可变 Map)

不可变 Map 是 Scala 推荐优先使用的类型(线程安全、无副作用),所有「修改操作」都会返回新 Map,原 Map 保持不变。

1. 初始化 Map

scala

import scala.collection.immutable.{Map, SortedMap}
import scala.collection.mutable.{Map => MutableMap, LinkedHashMap}

// 1. 不可变 Map(默认):直接用 Map(键值对)
val fruitMap: Map[String, Int] = Map("苹果" -> 10, "香蕉" -> 20, "橙子" -> 15)
// 等价写法(元组形式):Map(("苹果", 10), ("香蕉", 20), ("橙子", 15))

// 2. 空 Map 初始化
val emptyMap: Map[String, Int] = Map.empty[String, Int]

// 3. 不可变 SortedMap(按 Key 自然排序,String 字典序、Int 数字序)
val sortedMap: SortedMap[String, Int] = SortedMap("香蕉" -> 20, "苹果" -> 15, "橙子" -> 12)
println(sortedMap) // 输出:Map(苹果 -> 15, 橙子 -> 12, 香蕉 -> 20)(字典序)

// 4. 可变 Map 初始化(需显式导入)
val mutableFruitMap: MutableMap[String, Int] = MutableMap("苹果" -> 10, "香蕉" -> 20)

// 5. 可变 LinkedHashMap(保留插入顺序)
val linkedMap: LinkedHashMap[String, Int] = LinkedHashMap("苹果" -> 10, "香蕉" -> 20)

2. 增:+、++(不可变 Map)

不可变 Map 无 put 方法,通过 +(添加单个键值对)、++(批量添加)返回新 Map:

scala

val map1: Map[String, Int] = Map("苹果" -> 10)

// 1. 添加单个键值对(Key 不存在则新增,存在则覆盖)
val map2 = map1 + ("香蕉" -> 20) // 结果:Map(苹果 -> 10, 香蕉 -> 20)
val map3 = map2 + ("苹果" -> 15) // 覆盖苹果的值:Map(苹果 -> 15, 香蕉 -> 20)

// 2. 批量添加(合并另一个 Map)
val otherMap = Map("橙子" -> 12, "葡萄" -> 8)
val map4 = map3 ++ otherMap // 结果:Map(苹果 -> 15, 香蕉 -> 20, 橙子 -> 12, 葡萄 -> 8)

// 3. 添加多个键值对(用 + (k1->v1, k2->v2, ...))
val map5 = map4 + ("芒果" -> 25, "榴莲" -> 30)

3. 删:-、--(不可变 Map)

通过 -(key)(删除单个 Key)、--(keys)(批量删除 Key)返回新 Map:

scala

val map: Map[String, Int] = Map("苹果" -> 15, "香蕉" -> 20, "橙子" -> 12, "葡萄" -> 8)

// 1. 删除单个 Key
val map1 = map - "香蕉" // 结果:Map(苹果 -> 15, 橙子 -> 12, 葡萄 -> 8)

// 2. 批量删除多个 Key(传入集合)
val map2 = map -- List("橙子", "葡萄") // 结果:Map(苹果 -> 15, 香蕉 -> 20)

// 3. 删除不存在的 Key(无影响,返回原 Map)
val map3 = map - "梨" // 结果:与原 map 一致

4. 查:get、apply、contains、getOrElse

Scala Map 提供多种查询方式,推荐用 get(返回 Option,避免空指针):

scala

val map: Map[String, Int] = Map("苹果" -> 15, "香蕉" -> 20, "橙子" -> 12)

// 1. apply(key):直接获取 Value(Key 不存在抛 NoSuchElementException,慎用)
val appleCount: Int = map("苹果") // 15
// val pearCount = map("梨") // 抛异常:NoSuchElementException

// 2. get(key):返回 Option[V](存在则 Some(value),不存在则 None),安全推荐
val bananaOpt: Option[Int] = map.get("香蕉") // Some(20)
val pearOpt: Option[Int] = map.get("梨")     // None

// 3. getOrElse(key, 默认值):Key 不存在返回默认值,简化 Option 处理
val orangeCount: Int = map.getOrElse("橙子", 0) // 12
val pearCount: Int = map.getOrElse("梨", 0)     // 0(默认值)

// 4. contains(key):判断 Key 是否存在
val hasGrape: Boolean = map.contains("葡萄") // false
val hasApple: Boolean = map.contains("苹果") // true

// 5. 其他查询:获取所有 Key/Value
val keys: Iterable[String] = map.keys // 所有 Key:苹果、香蕉、橙子
val values: Iterable[Int] = map.values // 所有 Value:15、20、12
val size: Int = map.size // 键值对数量:3

5. 改:不可变 Map 无直接修改(需通过 + 覆盖)

不可变 Map 无法修改原有键值对,只能通过 + 覆盖已有 Key 的 Value,返回新 Map:

scala

val map: Map[String, Int] = Map("苹果" -> 15, "香蕉" -> 20)
val updatedMap = map + ("苹果" -> 25) // 覆盖苹果的值,返回新 Map:Map(苹果 -> 25, 香蕉 -> 20)

三、可变 Map 专属操作

可变 Map 支持直接修改原 Map(增删改无需返回新对象),核心 API 与不可变 Map 互补:

scala

import scala.collection.mutable.Map

val mutableMap: Map[String, Int] = Map("苹果" -> 15, "香蕉" -> 20)

// 1. 增/改:put(key, value)(Key 不存在新增,存在覆盖)
mutableMap.put("橙子", 12) // 新增:mutableMap 变为 Map(苹果 -> 15, 香蕉 -> 20, 橙子 -> 12)
mutableMap.put("苹果", 25) // 覆盖:苹果的值变为 25

// 2. 批量增/改:putAll(另一个 Map)
val otherMap = Map("葡萄" -> 8, "芒果" -> 25)
mutableMap.putAll(otherMap) // 批量添加:Map(苹果 -> 25, 香蕉 -> 20, 橙子 -> 12, 葡萄 -> 8, 芒果 -> 25)

// 3. 删:remove(key)(返回 Option[V],删除成功返回 Some(旧值),不存在返回 None)
val removedValue: Option[Int] = mutableMap.remove("香蕉") // Some(20),mutableMap 移除香蕉
mutableMap.remove("梨") // None(无影响)

// 4. 清空:clear()
// mutableMap.clear() // 清空所有键值对

// 5. 直接修改 Value:update(key, value)(等价于 put)
mutableMap.update("葡萄", 10) // 葡萄的值变为 10

// 6. 可变 LinkedHashMap(保留插入顺序)
val linkedMap = LinkedHashMap("a" -> 1, "b" -> 2)
linkedMap.put("c" -> 3)
println(linkedMap) // 输出:Map(a -> 1, b -> 2, c -> 3)(插入序)

四、Map 遍历(重点!函数式风格为主)

Scala 遍历 Map 支持多种方式,推荐用函数式风格(foreachmap 等),简洁高效:

1. 遍历键值对(最常用)

scala

val map: Map[String, Int] = Map("苹果" -> 15, "香蕉" -> 20, "橙子" -> 12)

// 1. 模式匹配遍历(最简洁,推荐)
map.foreach { case (key, value) =>
  println(s"$key$value") // 输出:苹果:15、香蕉:20、橙子:12(不可变 Map 无序,SortedMap 有序)
}

// 2. 遍历 Entry(类似 Java 的 EntrySet)
map.foreach(entry => {
  val key = entry._1 // 键(_1 是元组第一个元素)
  val value = entry._2 // 值(_2 是元组第二个元素)
  println(s"$key$value")
})

// 3. 分别遍历 Key 和 Value
map.keys.foreach(key => println(s"Key:$key")) // 遍历所有 Key
map.values.foreach(value => println(s"Value:$value")) // 遍历所有 Value

2. 函数式遍历(过滤、映射、排序)

Scala Map 支持流式操作(filtermapsorted 等),适合复杂处理:

scala

val map: Map[String, Int] = Map("苹果" -> 15, "香蕉" -> 20, "橙子" -> 12, "葡萄" -> 8)

// 1. 过滤:保留 Value > 12 的键值对
val filteredMap = map.filter { case (_, value) => value > 12 }
println(filteredMap) // 输出:Map(苹果 -> 15, 香蕉 -> 20)

// 2. 映射:将 Value 翻倍,返回新 Map
val doubledMap = map.map { case (key, value) => key -> value * 2 }
println(doubledMap) // 输出:Map(苹果 -> 30, 香蕉 -> 40, 橙子 -> 24, 葡萄 -> 16)

// 3. 排序:按 Value 降序(返回 List,因不可变 Map 无序)
val sortedList = map.toList.sortBy(-_._2) // -_._2 表示按 Value 降序
println(sortedList) // 输出:List((香蕉,20), (苹果,15), (橙子,12), (葡萄,8))

// 4. 统计:计算所有 Value 的总和
val total = map.values.sum
println(total) // 输出:55

// 5. 分组:按 Key 长度分组(Key 长度为键,Map 为值)
val grouped = map.groupBy(_._1.length)
println(grouped) // 输出:Map(2 -> Map(苹果 -> 15, 香蕉 -> 20, 橙子 -> 12), 2 -> Map(葡萄 -> 8))(中文占 1 个长度)

五、Scala 专属实用方法(函数式核心)

1. getOrElseUpdate(key, 默认值)(可变 Map 专属)

Key 不存在时,插入「键 - 默认值」并返回默认值;存在则直接返回 Value(适合懒加载场景):

scala

import scala.collection.mutable.Map

val mutableMap: Map[String, Int] = Map("苹果" -> 15)

// 香蕉不存在,插入并返回 20
val bananaCount = mutableMap.getOrElseUpdate("香蕉", 20) // 20,map 变为 Map(苹果 -> 15, 香蕉 -> 20)

// 苹果已存在,直接返回 15(不修改)
val appleCount = mutableMap.getOrElseUpdate("苹果", 25) // 15

2. flatMap(扁平化映射)

将每个键值对映射为多个键值对,再扁平化合并:

scala

val map: Map[String, Int] = Map("a" -> 2, "b" -> 3)

// 每个键值对映射为 (key, 1), (key, 2), ..., (key, value)
val flatMapResult = map.flatMap { case (key, value) =>
  (1 to value).map(i => key -> i)
}
println(flatMapResult) // 输出:Map(a -> 1, a -> 2, b -> 1, b -> 2, b -> 3)

3. foldLeft/foldRight(聚合操作)

对键值对进行聚合计算(如求和、拼接字符串):

scala

val map: Map[String, Int] = Map("苹果" -> 15, "香蕉" -> 20, "橙子" -> 12)

// 聚合 Value 总和(初始值 0,累加每个 Value)
val total = map.foldLeft(0) { case (acc, (_, value)) => acc + value }
println(total) // 输出:47

// 拼接 Key 字符串(初始值 "", 拼接每个 Key)
val keyStr = map.foldLeft("") { case (acc, (key, _)) => acc + key + "、" }
println(keyStr) // 输出:"苹果、香蕉、橙子、"(可通过 dropRight(1) 去掉末尾逗号)

4. exists/forall(判断条件)

  • exists:是否存在满足条件的键值对
  • forall:是否所有键值对都满足条件

scala

val map: Map[String, Int] = Map("苹果" -> 15, "香蕉" -> 20, "橙子" -> 12)

// 是否存在 Value > 18 的键值对
val hasBigValue = map.exists { case (_, value) => value > 18 } // true(香蕉 20)

// 是否所有 Value 都 > 5
val allBig = map.forall { case (_, value) => value > 5 } // true

5. to 转换(Map 与其他集合互转)

scala

val map: Map[String, Int] = Map("苹果" -> 15, "香蕉" -> 20)

// 1. Map -> List(元组列表)
val list: List[(String, Int)] = map.toList // List((苹果,15), (香蕉,20))

// 2. Map -> Array
val array: Array[(String, Int)] = map.toArray

// 3. List -> Map(需确保 List 中 Key 唯一,否则后面的覆盖前面的)
val list2 = List(("橙子", 12), ("葡萄", 8))
val map2: Map[String, Int] = list2.toMap

六、SortedMap 自定义排序

SortedMap 默认按 Key 的自然顺序排序(如 String 字典序、Int 数字序),也可通过 Ordering 自定义排序规则:

scala

import scala.collection.immutable.SortedMap

// 1. 按 Key 长度降序排序(String 类型 Key)
val customOrdering = Ordering.by[String, Int](_.length).reverse // 长度降序
val sortedMap: SortedMap[String, Int] = SortedMap(
  "香蕉" -> 20,   // 长度 2
  "苹果" -> 15,   // 长度 2
  "猕猴桃" -> 8   // 长度 3
)(customOrdering)

println(sortedMap) // 输出:Map(猕猴桃 -> 8, 香蕉 -> 20, 苹果 -> 15)(长度降序)

// 2. 按 Value 降序排序(通过 toList 转换后排序,因 SortedMap 只按 Key 排序)
val map = Map("苹果" -> 15, "香蕉" -> 20, "橙子" -> 12)
val sortedByValue = map.toList.sortBy(-_._2).toMap
println(sortedByValue) // 输出:Map(香蕉 -> 20, 苹果 -> 15, 橙子 -> 12)

七、常见使用场景总结

  1. 无修改需求、线程安全:用默认的「不可变 Map」(简洁高效)
  2. 频繁增删改:用「可变 Map」(mutable.Map
  3. 需要按插入顺序遍历:用「可变 LinkedHashMap」
  4. 需要按 Key 排序:用「不可变 SortedMap」
  5. 避免空指针:用 get(返回 Option)或 getOrElse(指定默认值)
  6. 懒加载场景(不存在则初始化):用可变 Map 的 getOrElseUpdate
  7. 统计 / 聚合计算:用 foldLeft/sum/groupBy
  8. 复杂过滤 / 转换:用 filter/map/flatMap

八、注意事项

  1. Key 的唯一性:无论是可变还是不可变 Map,Key 重复都会覆盖原有值(无警告)。
  2. null 值支持:Scala Map 的 Key 和 Value 都可以为 null,但不推荐(建议用 Option 表示缺失值)。
  3. 不可变 Map 的「修改」本质:不可变 Map 的 +/- 操作不会修改原对象,而是返回新 Map,频繁修改会产生临时对象(需注意性能)。
  4. 线程安全:不可变 Map 天然线程安全(无状态);可变 Map 非线程安全,多线程环境需用 ConcurrentHashMap(需导入 java.util.concurrent.ConcurrentHashMap 或 Scala 封装类)。
  5. 模式匹配的灵活性:遍历 Map 时,模式匹配(case (key, value))比直接用 _1/_2 更易读,推荐优先使用。