Scala中的map映射

88 阅读5分钟

(一)Map 的定义和创建

Map 创建格式:

1.不可变 Map:val mapName: Map[KeyType, ValueType] = Map(key1 -> value1, key2 -> value2,...)

2.可变 Map(需导入scala.collection.mutable.Map):val mapName:

mutable.Map[KeyType, ValueType] = mutable.Map(key1 -> value2,...)

1. Map 的基本定义

不可变 Map(默认)

// 方式1:使用 -> 操作符
val map1 = Map("name" -> "Alice", "age" -> 25, "city" -> "Beijing")

// 方式2:使用元组形式
val map2 = Map(("name", "Alice"), ("age", 25), ("city", "Beijing"))

// 空Map
val emptyMap = Map.empty[String, Int]

可变 Map

import scala.collection.mutable.Map

// 创建可变Map
val mutableMap = Map("name" -> "Alice", "age" -> 25)

// 创建空的可变Map
val emptyMutableMap = Map.empty[String, Any]

2. Map 的创建示例

object MapExample {
    def main(args: Array[String]): Unit = {
        // 不可变Map
        val student = Map(
            "id" -> 1001,
            "name" -> "张三",
            "score" -> 95.5
        )
        println(s"不可变Map: $student")
        
        // 可变Map
        import scala.collection.mutable.Map
        val mutableStudent = Map(
            "id" -> 1002,
            "name" -> "李四"
        )
        println(s"可变Map: $mutableStudent")
    }
}

3. Map 的特性和规则

键的唯一性

val map = Map("a" -> 1, "b" -> 2, "a" -> 3)  // 结果: Map(a -> 3, b -> 2)
println(map)  // 后面的键值会覆盖前面的相同键

类型推断

// Scala会自动推断类型
val map1 = Map(1 -> "one", 2 -> "two")        // Map[Int, String]
val map2 = Map("x" -> 1.0, "y" -> 2.0)       // Map[String, Double]

4. 创建Map的不同方式

使用 apply 方法

val map1 = Map.apply("a" -> 1, "b" -> 2)

从其他集合转换

// 从List创建
val list = List(("a", 1), ("b", 2))
val mapFromList = list.toMap

// 从数组创建
val array = Array(("x", 10), ("y", 20))
val mapFromArray = array.toMap

使用 zip 方法

val keys = List("name", "age", "city")
val values = List("Alice", 25, "Beijing")
val zippedMap = keys.zip(values).toMap

5. 完整示例

object MapCreationDemo {
    def main(args: Array[String]): Unit = {
        // 1. 基本创建
        val capitals = Map(
            "China" -> "Beijing",
            "Japan" -> "Tokyo", 
            "USA" -> "Washington"
        )
        println(s"首都映射: $capitals")
        
        // 2. 可变Map创建
        import scala.collection.mutable.Map
        val shoppingCart = Map(
            "apple" -> 3,
            "banana" -> 5,
            "orange" -> 2
        )
        println(s"购物车: $shoppingCart")
        
        // 3. 空Map创建
        val emptyImmutable = Map.empty[String, Int]
        val emptyMutable = Map.empty[String, String]
        
        println(s"空不可变Map: $emptyImmutable")
        println(s"空可变Map: $emptyMutable")
        
        // 4. 类型验证
        println(s"capitals类型: ${capitals.getClass}")
        println(s"shoppingCart类型: ${shoppingCart.getClass}")
    }
}

6. 运行结果

首都映射: Map(China -> Beijing, Japan -> Tokyo, USA -> Washington)
购物车: Map(apple -> 3, banana -> 5, orange -> 2)
空不可变Map: Map()
空可变Map: Map()
capitals类型: class scala.collection.immutable.Map$Map3
shoppingCart类型: class scala.collection.mutable.HashMap

关键点总结

  1. 默认不可变:直接使用 Map() 创建的是不可变Map
  2. 键唯一性:相同的键会覆盖之前的值
  3. 类型安全:Scala有强大的类型推断能力
  4. 灵活创建:支持多种创建方式,包括从其他集合转换

可变 vs 不可变 Map 对比

特性可变Map不可变Map
包路径scala.collection.mutable.Mapscala.collection.immutable.Map
修改方式直接修改原Map创建新Map
操作符+=-=+-
线程安全不安全安全
性能修改操作快修改需要复制

(二)Map 的常用方法 - 添加元素

可变 Map 的添加方法

import scala.collection.mutable.Map

object MapAddDemo {
    def main(args: Array[String]): Unit = {
        val scores = Map("Alice" -> 90, "Bob" -> 85)
        
        // 1. 使用 += 添加单个元素
        scores += ("Charlie" -> 95)
        
        // 2. 使用 += 添加多个元素
        scores += ("David" -> 88, "Eve" -> 92)
        
        // 3. 使用 put 方法添加(返回Option)
        val previousValue = scores.put("Alice", 96)  // 更新Alice的分数
        println(s"Alice原来的分数: $previousValue")  // 输出: Some(90)
        
        // 4. 使用 update 方法
        scores.update("Frank", 87)
        scores("Grace") = 93  // 等价于 update
        
        // 5. 使用 ++= 添加另一个Map
        val newScores = Map("Henry" -> 89, "Ivy" -> 91)
        scores ++= newScores
        
        println(s"添加后的Map: $scores")
    }
}

不可变 Map 的添加方法

object ImmutableMapAdd {
    def main(args: Array[String]): Unit = {
        val original = Map("A" -> 1, "B" -> 2)
        
        // 1. 使用 + 添加单个元素(返回新Map)
        val withC = original + ("C" -> 3)
        
        // 2. 使用 + 添加多个元素
        val withMore = original + ("D" -> 4, "E" -> 5)
        
        // 3. 使用 ++ 合并另一个Map
        val additional = Map("F" -> 6, "G" -> 7)
        val merged = original ++ additional
        
        println(s"原Map: $original")
        println(s"添加C后: $withC")
        println(s"添加多个后: $withMore")
        println(s"合并后: $merged")
    }
}

(三)Map 的常用方法 - 查询元素

object MapQueryDemo {
    def main(args: Array[String]): Unit = {
        val student = Map(
            "name" -> "张三",
            "age" -> 20,
            "score" -> 95.5,
            "city" -> "北京"
        )
        
        // 1. 使用 get 方法(返回Option)
        val nameOption = student.get("name")
        println(s"姓名: $nameOption")  // 输出: Some(张三)
        
        // 2. 直接使用键访问(如果键不存在会抛出异常)
        val age = student("age")
        println(s"年龄: $age")
        
        // 3. 使用 getOrElse 提供默认值
        val gender = student.getOrElse("gender", "未知")
        println(s"性别: $gender")  // 输出: 未知
        
        // 4. 使用 contains 检查键是否存在
        if (student.contains("score")) {
            println("成绩信息存在")
        }
        
        // 5. 使用 apply 方法(与直接访问相同)
        val city = student.apply("city")
        println(s"城市: $city")
        
        // 6. 获取所有键和值
        println(s"所有键: ${student.keys}")
        println(s"所有值: ${student.values}")
        println(s"键集合: ${student.keySet}")
        
        // 7. 安全访问示例
        val phone = student.get("phone") match {
            case Some(value) => value
            case None => "未提供"
        }
        println(s"电话: $phone")
    }
}

(四)Map 的常用方法 - 删除元素(仅针对可变 Map)

import scala.collection.mutable.Map

object MapRemoveDemo {
    def main(args: Array[String]): Unit = {
        val mutableMap = Map(
            "A" -> 1, "B" -> 2, "C" -> 3, 
            "D" -> 4, "E" -> 5, "F" -> 6
        )
        println(s"原始Map: $mutableMap")
        
        // 1. 使用 -= 删除单个元素
        mutableMap -= "A"
        println(s"删除A后: $mutableMap")
        
        // 2. 使用 -= 删除多个元素
        mutableMap -= ("B", "C")
        println(s"删除B,C后: $mutableMap")
        
        // 3. 使用 remove 方法(返回被删除的值)
        val removedValue = mutableMap.remove("D")
        println(s"删除D, 返回值: $removedValue")  // 输出: Some(4)
        println(s"删除D后: $mutableMap")
        
        // 4. 删除不存在的键(不会报错)
        mutableMap -= "X"  // 安全,不会报错
        val noValue = mutableMap.remove("Y")
        println(s"删除不存在的键Y: $noValue")  // 输出: None
        
        // 5. 使用 --= 删除多个键
        val keysToRemove = Set("E", "F")
        mutableMap --= keysToRemove
        println(s"删除E,F后: $mutableMap")
        
        // 6. 清空整个Map
        mutableMap.clear()
        println(s"清空后: $mutableMap")
    }
}

(五)Map 的遍历

object MapTraversalDemo {
    def main(args: Array[String]): Unit = {
        val studentInfo = Map(
            "name" -> "李四",
            "age" -> 22,
            "major" -> "计算机科学",
            "score" -> 88.5,
            "city" -> "上海"
        )
        
        println("=== 遍历方式1: foreach ===")
        // 1. 使用 foreach 遍历
        studentInfo.foreach { case (key, value) =>
            println(s"键: $key, 值: $value")
        }
        
        println("\n=== 遍历方式2: for循环 ===")
        // 2. 使用 for 循环遍历
        for ((key, value) <- studentInfo) {
            println(s"$key -> $value")
        }
        
        println("\n=== 遍历方式3: 只遍历键 ===")
        // 3. 只遍历键
        studentInfo.keys.foreach { key =>
            println(s"键: $key")
        }
        
        println("\n=== 遍历方式4: 只遍历值 ===")
        // 4. 只遍历值
        studentInfo.values.foreach { value =>
            println(s"值: $value")
        }
        
        println("\n=== 遍历方式5: 使用迭代器 ===")
        // 5. 使用迭代器
        val iterator = studentInfo.iterator
        while (iterator.hasNext) {
            val (key, value) = iterator.next()
            println(s"$key: $value")
        }
        
        println("\n=== 遍历方式6: 带条件的遍历 ===")
        // 6. 带条件的遍历
        for ((key, value) <- studentInfo if key.startsWith("a")) {
            println(s"以'a'开头的键: $key -> $value")
        }
        
        println("\n=== 遍历方式7: 转换后遍历 ===")
        // 7. 转换后遍历
        studentInfo.map { case (key, value) =>
            s"${key.toUpperCase}: $value"
        }.foreach(println)
    }
}

完整综合示例

object MapCompleteDemo {
    def main(args: Array[String]): Unit = {
        import scala.collection.mutable.Map
        
        // 创建可变Map
        var inventory = Map(
            "apple" -> 10,
            "banana" -> 5,
            "orange" -> 8
        )
        
        println("初始库存:")
        inventory.foreach(println)
        
        // 添加新商品
        inventory += ("grape" -> 12)
        println("\n添加葡萄后:")
        inventory.foreach(println)
        
        // 查询库存
        println(s"\n苹果库存: ${inventory.getOrElse("apple", 0)}")
        println(s"是否存在香蕉: ${inventory.contains("banana")}")
        
        // 更新库存
        inventory("apple") = 15
        println(s"\n更新苹果库存后: ${inventory("apple")}")
        
        // 删除商品
        inventory -= "orange"
        println("\n删除橙子后:")
        inventory.foreach(println)
        
        // 遍历并处理
        println("\n库存报告:")
        for ((item, quantity) <- inventory) {
            val status = if (quantity > 10) "充足" else "需要补货"
            println(s"商品: $item, 数量: $quantity, 状态: $status")
        }
    }
}

运行结果示例

初始库存:
(apple,10)
(banana,5)
(orange,8)

添加葡萄后:
(apple,10)
(banana,5)
(orange,8)
(grape,12)

苹果库存: 10
是否存在香蕉: true

更新苹果库存后: 15

删除橙子后:
(apple,15)
(banana,5)
(grape,12)

库存报告:
商品: apple, 数量: 15, 状态: 充足
商品: banana, 数量: 5, 状态: 需要补货
商品: grape, 数量: 12, 状态: 充足

Map 关键点总结

1. Map 的基本概念

  • 键值对结构:每个键对应一个值,键是唯一的
  • 两种类型:可变Map (mutable.Map) 和不可变Map (immutable.Map)
  • 默认不可变:直接使用 Map() 创建的是不可变Map

2. 创建方式

// 不可变Map(默认)
val map1 = Map("a" -> 1, "b" -> 2)
val map2 = Map(("a", 1), ("b", 2))

// 可变Map
import scala.collection.mutable.Map
val mutableMap = Map("x" -> 10, "y" -> 20)

3. 添加元素的关键区别

可变Map

map += ("c" -> 3)        // 直接修改原Map
map.put("d", 4)          // 返回Option[原值]
map("e") = 5             // 直接赋值

不可变Map

val newMap = map + ("c" -> 3)    // 返回新Map,原Map不变
val merged = map1 ++ map2        // 合并Map

4. 查询元素的正确方式

安全查询(推荐)

val value = map.get("key")           // 返回 Option[T]
val value = map.getOrElse("key", default) // 提供默认值

直接查询(危险)

val value = map("key")    // 键不存在时抛出异常

检查存在性

if (map.contains("key")) { ... }

5. 删除元素的限制

仅可变Map支持删除

map -= "key"              // 删除单个键
map -= ("a", "b")         // 删除多个键
map.remove("key")         // 返回被删除的值Option
map.clear()              // 清空Map

不可变Map的"删除"

val newMap = map - "key"  // 返回删除指定键后的新Map

6. 遍历的多种方式

// 遍历键值对
map.foreach { case (k, v) => ... }

// for循环遍历
for ((k, v) <- map) { ... }

// 分别遍历键和值
map.keys.foreach(...)
map.values.foreach(...)

7. 重要特性

键的唯一性

val map = Map("a" -> 1, "a" -> 2)  // 结果: Map(a -> 2)
// 后面的键值会覆盖前面的相同键

类型安全

val map = Map("name" -> "Alice", "age" -> 25)
// Scala自动推断为 Map[String, Any]

性能特点

  • 查找高效:基于哈希表,平均O(1)时间复杂度
  • 无序性:不保证元素的顺序(LinkedHashMap除外)

8. 使用场景建议

使用可变Map当:

  • 需要频繁增删改操作
  • 在单线程环境下
  • 对性能要求较高

使用不可变Map当:

  • 数据基本不变
  • 多线程环境
  • 函数式编程风格
  • 需要保证数据一致性

9. 线程安全考虑

  • 不可变Map:天然线程安全
  • 可变Map:非线程安全,需要外部同步

10. 常见陷阱

  1. 直接访问不存在的键:会抛出异常,应用 getgetOrElse
  2. 混淆可变和不可变操作:注意 +=+ 的区别
  3. 期望保持顺序:普通Map不保证顺序,需要顺序用 ListMapLinkedHashMap

最佳实践

  1. 优先使用不可变Map
  2. 使用 getOrElse 进行安全访问
  3. 明确导入可变包名避免混淆
  4. 合理选择Map类型基于使用场景

这些关键点涵盖了Map的核心概念和日常使用中的重要注意事项。