【scala中文源码系列】scala.collection可变不可变Map适用场景优缺点用法示例源码分析

scala.collection可变不可变Map区别适用场景优缺点用法示例源码分析

一、可变scala.collection.mutable.Map

1.适用场景

scala.collection.mutable.Map适用于以下场景:

  1. 需要频繁修改映射内容:可变映射允许添加、更新和删除键值对,适用于需要经常修改映射内容的场景。例如,在构建缓存或实时数据结构时,可变映射可以提供高效的修改操作。

  2. 需要动态地调整映射大小:可变映射的大小可以根据需要进行动态调整,可以随时添加或删除键值对。这在需要根据运行时条件动态调整映射大小的场景中非常有用。

  3. 不需要线程安全性保证:如果应用程序是单线程的,或者在多线程环境下使用适当的同步机制来确保线程安全性,那么可变映射是合适的选择。

  4. 需要灵活的数据结构操作:可变映射提供了丰富的方法和操作,如添加、更新、删除键值对,遍历映射等。这使得它们非常适合需要灵活操作映射的场景。

  5. 性能优化要求较高:由于可变映射具有高效的修改操作,适用于对性能有较高要求的场景。在大规模数据处理和算法中,可变映射可以提供更好的性能。

总而言之,scala.collection.mutable.Map适用于需要频繁修改映射内容、动态调整映射大小、灵活操作数据结构和对性能有较高要求的场景。但请注意,在多线程环境中使用可变映射时,需要适当的同步机制来确保线程安全性。

2.优缺点

scala.collection.mutable.Map有以下优点:

  1. 可变性:可变映射是可以修改的,可以通过添加、更新或删除操作来改变映射中的键值对。这使得它们在需要频繁修改映射内容的场景下非常有用。

  2. 高效的修改操作:由于可变映射是可以修改的,添加、更新或删除操作具有高效的时间复杂度。这使得可变映射在需要频繁修改映射内容的场景下性能更好。

  3. 灵活性:可变映射提供了丰富的方法和操作,可以满足不同的需求。例如,添加、更新或删除键值对,遍历映射,获取映射的大小等等。

  4. 可变性支持:可变映射对于某些特定的算法和数据结构非常重要,例如,在一些图算法中,需要动态地添加或删除边,这时可变映射非常有用。

然而,scala.collection.mutable.Map也有一些缺点:

  1. 线程安全性:可变映射不是线程安全的,多个线程同时修改映射可能导致数据冲突和不一致性。如果在多线程环境中使用可变映射,需要适当的同步机制来保证数据的一致性。

  2. 可变状态的复杂性:可变映射在修改过程中可能导致状态的复杂性增加。如果不正确地管理和维护映射的状态,可能会引入错误或导致不一致的结果。

  3. 数据共享困难:由于可变映射是可以修改的,多个引用共享同一个映射可能导致数据冲突和意外修改。因此,在需要共享数据的情况下,需要谨慎处理可变映射。

综上所述,scala.collection.mutable.Map的优点包括可变性、高效的修改操作、灵活性和可变性支持,但它也有一些缺点,如线程安全性、可变状态的复杂性和数据共享困难。根据具体的使用场景和需求,我们可以选择是否使用可变映射。

3.示例

下面是几个scala.collection.mutable.Map的示例,每个示例都包含了完整的代码和输出结果。

示例1:创建和访问可变映射

import scala.collection.mutable

object MutableMapExample extends App {
  // 创建一个可变映射
  val map = mutable.Map("One" -> 1, "Two" -> 2, "Three" -> 3)

  // 访问映射中的值
  println(map("One"))    // 输出: 1

  // 更新映射中的值
  map("Two") = 22

  // 添加新的键值对到映射
  map += ("Four" -> 4)

  // 输出更新后的映射内容
  println(map) // 输出: Map(One -> 1, Two -> 22, Three -> 3, Four -> 4)
}

输出结果:

1
Map(One -> 1, Two -> 22, Three -> 3, Four -> 4)

示例2:遍历可变映射

import scala.collection.mutable

object IterateMutableMap extends App {
  // 创建一个可变映射
  val map = mutable.Map("One" -> 1, "Two" -> 2, "Three" -> 3)

  // 使用foreach遍历映射
  map.foreach { case (key, value) =>
    println(s"Key: $key, Value: $value")
  }
}

输出结果:

Key: One, Value: 1
Key: Two, Value: 2
Key: Three, Value: 3

示例3:删除映射中的键值对

import scala.collection.mutable

object RemoveFromMutableMap extends App {
  // 创建一个可变映射
  val map = mutable.Map("One" -> 1, "Two" -> 2, "Three" -> 3)

  // 删除指定键的键值对
  map -= "Two"

  // 输出删除后的映射内容
  println(map) // 输出: Map(One -> 1, Three -> 3)
}

输出结果:

Map(One -> 1, Three -> 3)

这些示例演示了如何创建、访问、遍历和修改可变映射。你可以根据自己的需求修改和扩展这些示例。注意,可变映射是可以修改的,并且在多线程环境中需要适当的同步机制来保证数据的一致性。

4.源码

这段代码定义了scala.collection.mutable.Map,它是一个可以被修改的映射接口。该接口继承自Iterable[(A, B)]scala.collection.Map[A, B]MapLike[A, B, Map[A, B]],并且实现了mutable.Map特质。

具体来说,Map接口提供了对可变映射的常见操作和功能。它包含了添加、更新和删除键值对的方法,以及其他操作如遍历映射等。此外,Map还提供了一些辅助方法,如设置默认值和创建空映射等。

同时,Map对象是一个伴生对象,它提供了创建和操作可变映射的工厂方法和辅助方法。默认的实现类是HashMap

以上是scala.collection.mutable.Map接口及其伴生对象的简要介绍。你可以使用这个接口来创建、操作和处理可变的映射。

package scala
package collection
package mutable

import generic._

/** 
 *  可变映射的基础特质。
 *  $mapNote
 *  $mapTags
 *  @since 1.0
 *  @author  Matthias Zenger
 */
trait Map[A, B]
  extends Iterable[(A, B)]
//     with GenMap[A, B]
     with scala.collection.Map[A, B]
     with MapLike[A, B, Map[A, B]] {

  override def empty: Map[A, B] = Map.empty

  override def seq: Map[A, B] = this

  /** 
   *  在给定的默认函数下返回相同的映射。
   *
   *  调用转换器方法(例如`map`)不会保留默认值。
   *
   *  @param d     将键映射到值的函数,用于不存在的键
   *  @return      带有默认值的映射的包装器
   */
  def withDefault(d: A => B): mutable.Map[A, B] = new Map.WithDefault[A, B](this, d)

  /** 
   *  在给定的默认值下返回相同的映射。
   *
   *  调用转换器方法(例如`map`)不会保留默认值。
   *
   *  @param d     用于不存在的键的默认值
   *  @return      带有默认值的映射的包装器
   */
  def withDefaultValue(d: B): mutable.Map[A, B] = new Map.WithDefault[A, B](this, x => d)
}

/** 
 *  $factoryInfo
 *  当前默认的`Map`的实现是`HashMap`。
 *  @define coll 可变映射
 *  @define Coll `mutable.Map`
 */
object Map extends MutableMapFactory[Map] {
  /** $canBuildFromInfo */
  implicit def canBuildFrom[A, B]: CanBuildFrom[Coll, (A, B), Map[A, B]] = new MapCanBuildFrom[A, B]

  def empty[A, B]: Map[A, B] = new HashMap[A, B]

  class WithDefault[A, B](underlying: Map[A, B], d: A => B) extends scala.collection.Map.WithDefault(underlying, d) with Map[A, B] {
    override def += (kv: (A, B)) = {underlying += kv; this}
    def -= (key: A) = {underlying -= key; this}
    override def empty = new WithDefault(underlying.empty, d)
    override def updated[B1 >: B](key: A, value: B1): WithDefault[A, B1] = new WithDefault[A, B1](underlying.updated[B1](key, value), d)
    override def + [B1 >: B](kv: (A, B1)): WithDefault[A, B1] = updated(kv._1, kv._2)
    override def - (key: A): WithDefault[A, B] = new WithDefault(underlying - key, d)

    /** 
     *  如果这些方法没有被重写以传递底层映射,
     *  连续调用withDefault*将没有效果。
     */
    override def withDefault(d: A => B): mutable.Map[A, B] = new WithDefault[A, B](underlying, d)
    override def withDefaultValue(d: B): mutable.Map[A, B] = new WithDefault[A, B](underlying, x => d)
  }
}

/** 
 *  `Map`特质的显式实例化,以减少子类中的类文件大小。
 */
abstract class AbstractMap[A, B] extends scala.collection.AbstractMap[A, B] with Map[A, B]

二、不可变scala.collection.immutable.Map

1.适用场景

scala.collection.immutable.Map适用于以下场景:

  1. 快速查找:不可变映射提供了快速的键值对查找操作,通过键来获取对应的值。这在需要根据键快速查找值的场景非常有用。

  2. 数据共享:不可变映射是线程安全的,多个线程可以同时读取不可变映射中的数据,而不需要额外的同步机制。这使得不可变映射非常适合在多线程环境中共享数据。

  3. 函数式编程:不可变映射是函数式编程的核心概念之一,它强调无副作用和不可变性。使用不可变映射可以更容易地编写纯函数,并且避免了因为修改映射而引入的错误。

  4. 数据缓存:不可变映射提供了高效的查询操作,可以用于缓存计算结果或者存储静态数据。由于不可变映射的特性,缓存的数据不会被意外修改。

总而言之,不可变映射适用于需要高效、线程安全且不可变的键值对存储和访问的场景。它提供了对数据的高效操作和保护,并且能够更好地支持函数式编程的理念。

2.优缺点

scala.collection.immutable.Map有以下优点:

  1. 不可变性:不可变映射是不可变的,一旦创建后就不能修改。这使得它们线程安全,多个线程可以同时读取映射中的数据而无需额外的同步机制。

  2. 高效的查找操作:不可变映射提供了快速的键值对查找操作,通过键来获取对应的值。这使得查找操作的时间复杂度为O(1)。

  3. 数据共享:由于不可变性的特性,不可变映射可以被多个引用共享,而不会导致数据冲突或意外修改。

  4. 函数式编程支持:不可变映射是函数式编程的核心概念之一,它强调无副作用和不可变性。使用不可变映射可以更容易地编写纯函数,并且避免了因为修改映射而引入的错误。

  5. 易于理解和调试:由于不可变映射的不可变性,其状态在整个生命周期内保持不变。这使得代码更加可预测,易于理解和调试。

然而,scala.collection.immutable.Map也有一些缺点:

  1. 修改昂贵:由于不可变映射的不可变性,每次添加、更新或删除操作都会创建一个新的映射对象。这可能导致在频繁修改映射时的性能问题。

  2. 空间占用:由于每次修改操作都会创建一个新的映射对象,不可变映射可能需要更多的内存空间来存储多个版本的映射。

  3. 迭代顺序固定:不可变映射的迭代顺序是固定的,并且取决于键值对的插入顺序。这可能会导致一些应用场景中的不灵活性。

综上所述,scala.collection.immutable.Map的优点包括不可变性、高效的查找操作、数据共享和函数式编程支持,但它也有一些缺点,如修改昂贵和空间占用。根据具体的使用场景和需求,我们可以选择是否使用不可变映射。

3.示例

下面是几个scala.collection.immutable.Map的示例,每个示例都包含了完整的代码和输出结果。

示例1:创建和访问不可变映射

import scala.collection.immutable.Map
object ImmutableMapExample extends App {
  // 创建一个不可变映射
  val map = Map("One" -> 1, "Two" -> 2, "Three" -> 3)

  // 访问映射中的值
  println(map("One"))    // 输出: 1
  println(map.get("Two")) // 输出: Some(2)
  println(map.get("Four")) // 输出: None
}

输出结果:

1
Some(2)
None

示例2:遍历不可变映射

import scala.collection.immutable.Map
object IterateImmutableMap extends App {
  // 创建一个不可变映射
  val map = Map("One" -> 1, "Two" -> 2, "Three" -> 3)

  // 使用foreach遍历映射
  map.foreach { case (key, value) =>
    println(s"Key: $key, Value: $value")
  }
}

输出结果:

Key: One, Value: 1
Key: Two, Value: 2
Key: Three, Value: 3

示例3:修改不可变映射

import scala.collection.immutable.Map
object UpdateImmutableMap extends App {
  // 创建一个不可变映射
  var map = Map("One" -> 1, "Two" -> 2, "Three" -> 3)

  // 添加键值对到映射
  map += ("Four" -> 4)

  // 输出映射内容
  println(map) // 输出: Map(One -> 1, Two -> 2, Three -> 3, Four -> 4)

  // 移除指定键的键值对
  map -= "Three"

  // 输出移除后的映射内容
  println(map) // 输出: Map(One -> 1, Two -> 2, Four -> 4)
}

输出结果:

Map(One -> 1, Two -> 2, Three -> 3, Four -> 4)
Map(One -> 1, Two -> 2, Four -> 4)

4.源码

scala.collection.immutable.Map是一个不可变的、有序的映射接口。该接口提供了对不可变映射的常见操作和功能。

具体来说,Map接口继承自Iterable[(A, B)]scala.collection.Map[A, B]MapLike[A, B, Map[A, B]],并且实现了immutable.Map特质。它要求具体的实现类提供以下方法的功能:

  • get(key: A): Option[B]:根据给定的键获取映射中对应的值。
  • iterator: Iterator[(A, B)]:返回映射的迭代器,以便遍历键值对。
  • + [B1 >: B](kv: (A, B1)): Map[A, B1]:将给定的键值对添加到映射,并返回一个新的映射。
  • - (key: A): Map[A, B]:从映射中移除指定的键,并返回一个新的映射。

此外,Map还提供了一些其他的方法,如emptytoMapseq等。

同时,Map对象是一个伴生对象,它提供了创建和操作不可变有序映射的工厂方法和辅助方法。

以上是scala.collection.immutable.Map接口及其伴生对象的简要介绍。你可以使用这个接口来创建、操作和处理不可变的有序映射。

package scala
package collection
package immutable

import generic._

/**
 * 不可变映射的通用特质。具体类必须提供`Map`中的抽象方法的功能:
 *
 * {{{
 *    def get(key: A): Option[B]
 *    def iterator: Iterator[(A, B)]
 *    def + [B1 >: B](kv: (A, B1)): Map[A, B1]
 *    def -(key: A): Map[A, B]
 * }}}
 *
 * @since 1
 */
trait Map[A, +B] extends Iterable[(A, B)]
                    with scala.collection.Map[A, B]
                    with MapLike[A, B, Map[A, B]] { self =>

  override def empty: Map[A, B] = Map.empty

  /** 将此$coll作为不可变映射返回。
   *
   * 不会构建新的映射;延迟集合仍然保持延迟。
   */
  @deprecatedOverriding("不可变映射在toMap上除了将自己强制转换为映射之外不应该做任何操作。",  "2.11.0")
  override def toMap[T, U](implicit ev: (A, B) <:< (T, U)): immutable.Map[T, U] =
    self.asInstanceOf[immutable.Map[T, U]]

  override def seq: Map[A, B] = this

  /** 具有给定默认函数的相同映射。
   * 注意:`get`、`contains`、`iterator`、`keys`等方法不受`withDefault`影响。
   *
   * 调用转换器方法(例如`map`)不会保留默认值。
   *
   * @param d     将键映射到值的函数,用于不存在的键
   * @return      具有默认值的映射的包装器
   */
  def withDefault[B1 >: B](d: A => B1): immutable.Map[A, B1] = new Map.WithDefault[A, B1](this, d)

  /** 具有给定默认值的相同映射。
   * 注意:`get`、`contains`、`iterator`、`keys`等方法不受`withDefaultValue`影响。
   *
   * 调用转换器方法(例如`map`)不会保留默认值。
   *
   * @param d     用于不存在的键的默认值
   * @return      具有默认值的映射的包装器
   */
  def withDefaultValue[B1 >: B](d: B1): immutable.Map[A, B1] = new Map.WithDefault[A, B1](this, x => d)

  /** 向此映射添加一个键/值对。
   * @param    key 键
   * @param    value 值
   * @return   添加了新绑定的新映射
   */
  override def updated [B1 >: B](key: A, value: B1): Map[A, B1]
  def + [B1 >: B](kv: (A, B1)): Map[A, B1]
}

/** $factoryInfo
 *  @define Coll `immutable.Map`
 *  @define coll 不可变映射
 */
object Map extends ImmutableMapFactory[Map] {

  /** $mapCanBuildFromInfo */
  implicit def canBuildFrom[A, B]: CanBuildFrom[Coll, (A, B), Map[A, B]] = new MapCanBuildFrom[A, B]

  def empty[A, B]: Map[A, B] = EmptyMap.asInstanceOf[Map[A, B]]

  class WithDefault[A, +B](underlying: Map[A, B], d: A => B) extends scala.collection.Map.WithDefault[A, B](underlying, d) with Map[A, B] {
    override def empty = new WithDefault(underlying.empty, d)
    override def updated[B1 >: B](key: A, value: B1): WithDefault[A, B1] = new WithDefault[A, B1](underlying.updated[B1](key, value), d)
    override def + [B1 >: B](kv: (A, B1)): WithDefault[A, B1] = updated(kv._1, kv._2)
    override def - (key: A): WithDefault[A, B] = new WithDefault(underlying - key, d)
    override def withDefault[B1 >: B](d: A => B1): immutable.Map[A, B1] = new WithDefault[A, B1](underlying, d)
    override def withDefaultValue[B1 >: B](d: B1): immutable.Map[A, B1] = new WithDefault[A, B1](underlying, x => d)
  }

  private object EmptyMap extends AbstractMap[Any, Nothing] with Map[Any, Nothing] with Serializable {
    override def size: Int = 0
    def get(key: Any): Option[Nothing] = None
    def iterator: Iterator[(Any, Nothing)] = Iterator.empty
    override def updated [B1] (key: Any, value: B1): Map[Any, B1] = new Map1(key, value)
    def + [B1](kv: (Any, B1)): Map[Any, B1] = updated(kv._1, kv._2)
    def - (key: Any): Map[Any, Nothing] = this
  }

  class Map1[A, +B](key1: A, value1: B) extends AbstractMap[A, B] with Map[A, B] with Serializable {
    override def size = 1
    def get(key: A): Option[B] =
      if (key == key1) Some(value1) else None
    def iterator = Iterator((key1, value1))
    override def updated [B1 >: B] (key: A, value: B1): Map[A, B1] =
      if (key == key1) new Map1(key1, value)
      else new Map2(key1, value1, key, value)
    def + [B1 >: B](kv: (A, B1)): Map[A, B1] = updated(kv._1, kv._2)
    def - (key: A): Map[A, B] =
      if (key == key1) Map.empty else this
    override def foreach[U](f: ((A, B)) =>  U): Unit = {
      f((key1, value1))
    }
  }

  class Map2[A, +B](key1: A, value1: B, key2: A, value2: B) extends AbstractMap[A, B] with Map[A, B] with Serializable {
    override def size = 2
    def get(key: A): Option[B] =
      if (key == key1) Some(value1)
      else if (key == key2) Some(value2)
      else None
    def iterator = Iterator((key1, value1), (key2, value2))
    override def updated [B1 >: B] (key: A, value: B1): Map[A, B1] =
      if (key == key1) new Map2(key1, value, key2, value2)
      else if (key == key2) new Map2(key1, value1, key2, value)
      else new Map3(key1, value1, key2, value2, key, value)
    def + [B1 >: B](kv: (A, B1)): Map[A, B1] = updated(kv._1, kv._2)
    def - (key: A): Map[A, B] =
      if (key == key1) new Map1(key2, value2)
      else if (key == key2) new Map1(key1, value1)
      else this
    override def foreach[U](f: ((A, B)) =>  U): Unit = {
      f((key1, value1)); f((key2, value2))
    }
  }

  class Map3[A, +B](key1: A, value1: B, key2: A, value2: B, key3: A, value3: B) extends AbstractMap[A, B] with Map[A, B] with Serializable {
    override def size = 3
    def get(key: A): Option[B] =
      if (key == key1) Some(value1)
      else if (key == key2) Some(value2)
      else if (key == key3) Some(value3)
      else None
    def iterator = Iterator((key1, value1), (key2, value2), (key3, value3))
    override def updated [B1 >: B] (key: A, value: B1): Map[A, B1] =
      if (key == key1)      new Map3(key1, value, key2, value2, key3, value3)
      else if (key == key2) new Map3(key1, value1, key2, value, key3, value3)
      else if (key == key3) new Map3(key1, value1, key2, value2, key3, value)
      else new Map4(key1, value1, key2, value2, key3, value3, key, value)
    def + [B1 >: B](kv: (A, B1)): Map[A, B1] = updated(kv._1, kv._2)
    def - (key: A): Map[A, B] =
      if (key == key1)      new Map2(key2, value2, key3, value3)
      else if (key == key2) new Map2(key1, value1, key3, value3)
      else if (key == key3) new Map2(key1, value1, key2, value2)
      else this
    override def foreach[U](f: ((A, B)) =>  U): Unit = {
      f((key1, value1)); f((key2, value2)); f((key3, value3))
    }
  }

  class Map4[A, +B](key1: A, value1: B, key2: A, value2: B, key3: A, value3: B, key4: A, value4: B) extends AbstractMap[A, B] with Map[A, B] with Serializable {
    override def size = 4
    def get(key: A): Option[B] =
      if (key == key1) Some(value1)
      else if (key == key2) Some(value2)
      else if (key == key3) Some(value3)
      else if (key == key4) Some(value4)
      else None
    def iterator = Iterator((key1, value1), (key2, value2), (key3, value3), (key4, value4))
    override def updated [B1 >: B] (key: A, value: B1): Map[A, B1] =
      if (key == key1)      new Map4(key1, value, key2, value2, key3, value3, key4, value4)
      else if (key == key2) new Map4(key1, value1, key2, value, key3, value3, key4, value4)
      else if (key == key3) new Map4(key1, value1, key2, value2, key3, value, key4, value4)
      else if (key == key4) new Map4(key1, value1, key2, value2, key3, value3, key4, value)
      else new HashMap + ((key1, value1), (key2, value2), (key3, value3), (key4, value4), (key, value))
    def + [B1 >: B](kv: (A, B1)): Map[A, B1] = updated(kv._1, kv._2)
    def - (key: A): Map[A, B] =
      if (key == key1)      new Map3(key2, value2, key3, value3, key4, value4)
      else if (key == key2) new Map3(key1, value1, key3, value3, key4, value4)
      else if (key == key3) new Map3(key1, value1, key2, value2, key4, value4)
      else if (key == key4) new Map3(key1, value1, key2, value2, key3, value3)
      else this
    override def foreach[U](f: ((A, B)) =>  U): Unit = {
      f((key1, value1)); f((key2, value2)); f((key3, value3)); f((key4, value4))
    }
  }
}

/** 显式实例化`Map`特质,以减小子类的类文件大小。 */
abstract class AbstractMap[A, +B] extends scala.collection.AbstractMap[A, B] with Map[A, B]

三、区别和选择

scala.collection.immutable.Mapscala.collection.mutable.Map是Scala集合库中用于表示Map的两个不同的类。它们之间的区别如下:

  1. 不可变性:immutable.Map是不可变的,即一旦创建就不能修改其内容。而mutable.Map是可变的,允许对其进行添加、删除或修改操作。

  2. 线程安全性:由于immutable.Map是不可变的,因此它在多线程环境中是线程安全的。而mutable.Map是可变的,需要额外的同步措施来确保在多线程环境中的正确使用。

  3. 操作返回值:immutable.Map中的大多数操作(如添加、删除、更新等)会返回一个新的Map对象,而不会改变原始的Map。这样可以确保不可变性。而mutable.Map中的大多数操作会直接修改原始的Map,并返回对自身的引用,以便进行链式调用。

  4. API支持:由于immutable.Map是不可变的,因此它提供了更丰富的功能和方法,包括查找、过滤、映射等。而mutable.Map的API相对较少,主要关注于修改操作。

在选择使用哪种类型的Map时,需要考虑以下几点:

  • 如果你的数据不会被修改,并且在多线程环境中使用,那么使用immutable.Map是一个不错的选择。它提供了丰富的功能和线程安全性。
  • 如果你需要对Map进行频繁的修改操作,或者希望能够在原地修改Map而不创建新的对象,那么使用mutable.Map更合适。它提供了直接修改原始Map的方法,并且在性能上可能更高效。

需要根据具体的使用场景和需求来选择合适的Map类型。在一般情况下,推荐首选immutable.Map,因为它更易于理解和使用,并且可以避免许多常见的错误。只有在性能要求较高或需要频繁的修改操作时,才考虑使用mutable.Map

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BigDataMLApplication

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值