Scala基础应用(7)- map/flatMap和filter

本文深入讲解Scala中的集合操作,包括map、flatMap、filter等方法的应用。解析这些方法如何帮助开发者高效处理集合数据,并通过示例代码展示它们的具体使用场景。

Scala基础应用(7)- map/flatMap和filter

本章主要介绍集合的map, flatten, flatMap, filter zip, fold, reduce等常用方法

map

map接受一个函数作为参数, 而此函数会接受一个参数为集合中元素, 所以此函数用于对集合中每一个元素进行处理后,产出其结果的集合。

val test = List("a", "b", "c")
test.map(x => x.toUpperCase)
或者 test.map(_.toUpperCase)

flatten

flatten 是将嵌套集合展开一层,注意Any类型的集合是不是展开的,但集合类型是可以展开的,即使是Any类型的集合类型。

List(List(List(1,2),List(3,4)), List(5,6)).flatten 可以展开一层,元法再次调用flatten进行再次展开,因为List(5,6)在第一次flatten时,已经展开,这个List会变成含有List和常数的List[Any]类型,下面可以进行再次展开

List(List(List(1,2),List(3,4)), List(List(5,6), List(7,8))).flatten.flatten  // 可以展开第二层

flatMap

flatMap实际上是对集合进行map, 然后将map函数处理的结果再flatten

List(List("a", "b"), List("c", "d")).flatMap (x => x)
与
List(List("a", "b"), List("c", "d")).flatten 返回的结果一样

只是前面一个式子中,x 参数是一个List集合

List(List(1,2),List(3,4)).flatMap(x=>x.map(x=>x*2))
通过上面这个式子,也可以看出x是集合,它有map功能,同时我们也能看出map返回的也是集合,而结果是展开的,所以可以理解为flatMap在执行完函数后,执行了flatten

filter

filter通过函数参数过滤掉函数返回false的元素

List("a", "b", "c").filter(x => x == "a")

zip

zip将两个集合进行合并,最终结果为较短集合的长度

val list1 = List("a", "b", "c", "d")
val list2 = List(1, 2, 3)
list1.zip(list2) 或者 list2.zip(list1)的长度都是3

可用zipAll来处理长度不同的两个集合,那如何处理较短的集合的填充呢?

list1.zipAll(list2, "填充1", "填充2")
如果list1较短,则用"填充1"填充,如果list2较短,则用"填充2"进行填充
上例将以"填充2"填充, 因为list2较短

fold

fold将集合元素进行处理,然后产生你想要的结果类型。

  • 样式

    集合变量.fold(初始值) { (累集结果变量,元素) => 处理函数体}
    
    如:
    List(1,2,3,4,5).fold(0) { (sum, item) => sum + item }
    返回15
    List(1,2,3,4,5).fold("a") { (sum, item) => sum + item.toString }
    返回 a12345
    
    注意:如果初始值类型和元素类型一至,返回类型也是该类型,否则为Any类型
  • foldLeft和foldRight分别从左开始fold和从右往左开始fold

    class Foo(val name: String, val age: Int, val genda: Symbol)
    object Foo {
        def apply(name: String, age: Int, genda: Symbol) = new Foo(name, age, genda)
    }
    
    val fooList = Foo("张三", 15, '男) :: Foo("李四", 20, '男) :: Foo("阿花", 18, '女) :: Nil
    
    val lst = fooList.foldLeft(List[String]()) {
        (z, f) =>
        val title = f.genda match {
            case '男 => "Mr."
            case '女 => "Ms."
        }
        println(z)
        z :+ s"$title ${f.name}, ${f.age}"
    }
    
    注意: foldLeft和foldRight产生的结果与初始值的类型相同
    
    另外:
    foldLeft的简写为 /:
    foldRight的简写为 :\
    
    (0/:(1 to 100)) (_+_)
    ((1 to 100) :\0) (_+_)

reduce

可以将reduce认为是fold的一种特殊情况,reduce的返回值类型必须与元素的类型相同

val list1 = List(1,2,3)
list1.reduce((sum,item) => sum + item)
/opt/jdk1.8.0_111/bin/java -javaagent:/zh/idea/lib/idea_rt.jar=36123:/zh/idea/bin -Dfile.encoding=UTF-8 -classpath /opt/jdk1.8.0_111/jre/lib/charsets.jar:/opt/jdk1.8.0_111/jre/lib/deploy.jar:/opt/jdk1.8.0_111/jre/lib/ext/cldrdata.jar:/opt/jdk1.8.0_111/jre/lib/ext/dnsns.jar:/opt/jdk1.8.0_111/jre/lib/ext/jaccess.jar:/opt/jdk1.8.0_111/jre/lib/ext/jfxrt.jar:/opt/jdk1.8.0_111/jre/lib/ext/localedata.jar:/opt/jdk1.8.0_111/jre/lib/ext/nashorn.jar:/opt/jdk1.8.0_111/jre/lib/ext/sunec.jar:/opt/jdk1.8.0_111/jre/lib/ext/sunjce_provider.jar:/opt/jdk1.8.0_111/jre/lib/ext/sunpkcs11.jar:/opt/jdk1.8.0_111/jre/lib/ext/zipfs.jar:/opt/jdk1.8.0_111/jre/lib/javaws.jar:/opt/jdk1.8.0_111/jre/lib/jce.jar:/opt/jdk1.8.0_111/jre/lib/jfr.jar:/opt/jdk1.8.0_111/jre/lib/jfxswt.jar:/opt/jdk1.8.0_111/jre/lib/jsse.jar:/opt/jdk1.8.0_111/jre/lib/management-agent.jar:/opt/jdk1.8.0_111/jre/lib/plugin.jar:/opt/jdk1.8.0_111/jre/lib/resources.jar:/opt/jdk1.8.0_111/jre/lib/rt.jar:/root/IdeaProjects/FinkDemo1/target/classes:/opt/scala-2.11.8/lib/scala-parser-combinators_2.11-1.0.4.jar:/opt/scala-2.11.8/lib/scala-library.jar:/opt/scala-2.11.8/lib/scala-actors-migration_2.11-1.1.0.jar:/opt/scala-2.11.8/lib/scala-xml_2.11-1.0.4.jar:/opt/scala-2.11.8/lib/scala-reflect.jar:/opt/scala-2.11.8/lib/scala-actors-2.11.0.jar:/opt/scala-2.11.8/lib/scala-swing_2.11-1.0.2.jar:/root/.m2/repository/org/apache/flink/flink-scala_2.11/1.13.0/flink-scala_2.11-1.13.0.jar:/root/.m2/repository/org/apache/flink/flink-core/1.13.0/flink-core-1.13.0.jar:/root/.m2/repository/org/apache/flink/flink-annotations/1.13.0/flink-annotations-1.13.0.jar:/root/.m2/repository/org/apache/flink/flink-metrics-core/1.13.0/flink-metrics-core-1.13.0.jar:/root/.m2/repository/org/apache/commons/commons-lang3/3.3.2/commons-lang3-3.3.2.jar:/root/.m2/repository/com/esotericsoftware/kryo/kryo/2.24.0/kryo-2.24.0.jar:/root/.m2/repository/com/esotericsoftware/minlog/minlog/1.2/minlog-1.2.jar:/root/.m2/repository/org/objenesis/objenesis/2.1/objenesis-2.1.jar:/root/.m2/repository/commons-collections/commons-collections/3.2.2/commons-collections-3.2.2.jar:/root/.m2/repository/org/apache/commons/commons-compress/1.20/commons-compress-1.20.jar:/root/.m2/repository/org/apache/flink/flink-shaded-guava/18.0-13.0/flink-shaded-guava-18.0-13.0.jar:/root/.m2/repository/org/apache/flink/flink-java/1.13.0/flink-java-1.13.0.jar:/root/.m2/repository/org/apache/commons/commons-math3/3.5/commons-math3-3.5.jar:/root/.m2/repository/org/apache/flink/flink-shaded-asm-7/7.1-13.0/flink-shaded-asm-7-7.1-13.0.jar:/root/.m2/repository/org/scala-lang/scala-reflect/2.11.12/scala-reflect-2.11.12.jar:/root/.m2/repository/org/scala-lang/scala-library/2.11.12/scala-library-2.11.12.jar:/root/.m2/repository/org/scala-lang/scala-compiler/2.11.12/scala-compiler-2.11.12.jar:/root/.m2/repository/org/scala-lang/modules/scala-xml_2.11/1.0.5/scala-xml_2.11-1.0.5.jar:/root/.m2/repository/org/scala-lang/modules/scala-parser-combinators_2.11/1.0.4/scala-parser-combinators_2.11-1.0.4.jar:/root/.m2/repository/org/slf4j/slf4j-api/1.7.15/slf4j-api-1.7.15.jar:/root/.m2/repository/com/google/code/findbugs/jsr305/1.3.9/jsr305-1.3.9.jar:/root/.m2/repository/org/apache/flink/force-shading/1.13.0/force-shading-1.13.0.jar:/root/.m2/repository/org/apache/flink/flink-streaming-scala_2.11/1.13.0/flink-streaming-scala_2.11-1.13.0.jar:/root/.m2/repository/org/apache/flink/flink-streaming-java_2.11/1.13.0/flink-streaming-java_2.11-1.13.0.jar:/root/.m2/repository/org/apache/flink/flink-file-sink-common/1.13.0/flink-file-sink-common-1.13.0.jar:/root/.m2/repository/org/apache/flink/flink-clients_2.11/1.13.0/flink-clients_2.11-1.13.0.jar:/root/.m2/repository/org/apache/flink/flink-runtime_2.11/1.13.0/flink-runtime_2.11-1.13.0.jar:/root/.m2/repository/org/apache/flink/flink-queryable-state-client-java/1.13.0/flink-queryable-state-client-java-1.13.0.jar:/root/.m2/repository/org/apache/flink/flink-hadoop-fs/1.13.0/flink-hadoop-fs-1.13.0.jar:/root/.m2/repository/commons-io/commons-io/2.7/commons-io-2.7.jar:/root/.m2/repository/org/apache/flink/flink-shaded-netty/4.1.49.Final-13.0/flink-shaded-netty-4.1.49.Final-13.0.jar:/root/.m2/repository/org/apache/flink/flink-shaded-jackson/2.12.1-13.0/flink-shaded-jackson-2.12.1-13.0.jar:/root/.m2/repository/org/apache/flink/flink-shaded-zookeeper-3/3.4.14-13.0/flink-shaded-zookeeper-3-3.4.14-13.0.jar:/root/.m2/repository/org/javassist/javassist/3.24.0-GA/javassist-3.24.0-GA.jar:/root/.m2/repository/com/typesafe/akka/akka-actor_2.11/2.5.21/akka-actor_2.11-2.5.21.jar:/root/.m2/repository/com/typesafe/config/1.3.3/config-1.3.3.jar:/root/.m2/repository/org/scala-lang/modules/scala-java8-compat_2.11/0.7.0/scala-java8-compat_2.11-0.7.0.jar:/root/.m2/repository/com/typesafe/akka/akka-stream_2.11/2.5.21/akka-stream_2.11-2.5.21.jar:/root/.m2/repository/org/reactivestreams/reactive-streams/1.0.2/reactive-streams-1.0.2.jar:/root/.m2/repository/com/typesafe/ssl-config-core_2.11/0.3.7/ssl-config-core_2.11-0.3.7.jar:/root/.m2/repository/com/typesafe/akka/akka-protobuf_2.11/2.5.21/akka-protobuf_2.11-2.5.21.jar:/root/.m2/repository/com/typesafe/akka/akka-slf4j_2.11/2.5.21/akka-slf4j_2.11-2.5.21.jar:/root/.m2/repository/org/clapper/grizzled-slf4j_2.11/1.3.2/grizzled-slf4j_2.11-1.3.2.jar:/root/.m2/repository/com/github/scopt/scopt_2.11/3.5.0/scopt_2.11-3.5.0.jar:/root/.m2/repository/org/xerial/snappy/snappy-java/1.1.8.3/snappy-java-1.1.8.3.jar:/root/.m2/repository/com/twitter/chill_2.11/0.7.6/chill_2.11-0.7.6.jar:/root/.m2/repository/com/twitter/chill-java/0.7.6/chill-java-0.7.6.jar:/root/.m2/repository/org/lz4/lz4-java/1.6.0/lz4-java-1.6.0.jar:/root/.m2/repository/org/apache/flink/flink-optimizer_2.11/1.13.0/flink-optimizer_2.11-1.13.0.jar:/root/.m2/repository/commons-cli/commons-cli/1.3.1/commons-cli-1.3.1.jar output.WordCountJob SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder". SLF4J: Defaulting to no-operation (NOP) logger implementation SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details. Process finished with exit code 0 无结果怎莫解决
07-03
课程名称: 大数据技术原理与应用 专业班级: 数科2304 姓 名: 仇一焯 学 号: 2023002253 指导教师: 陈泽华老师 2025年10月20日 实验 Spark Word Count 一 、实验内容: 基本要求: 完成Spark Word Count 二 、实验工具 虚拟机软件 VMware Workstation Pro17 操作系统 CentOS 7(64 位) ftp工具 XShell 8 Java 版本 jdk1.8.0_212 Hadoop 版本 hadoop-3.1.3 Spark版本 Spark 3.3.0 Maven 版本 Maven 3.6.3 三 、实验过程与截图 1.前置检查(必须执行) 1.1检查Spark集群状态 在 Master 节点(hadoop102)执行以下命令,确认 Master Worker 进程正常运行: - 执行节点:在 HDFS 的主节点(通常是 `hadoop102`)上执行。 - 命令(绝对路径): /opt/module/hadoop-3.1.3/sbin/start-dfs.sh 验证:在所有节点上执行 `jps` 命令。 - `hadoop102` 应看到 `NameNode` `DataNode`。 - `hadoop103` 应看到 `DataNode`。 - `hadoop104` 应看到 `DataNode` `SecondaryNameNode` 一键启动集群(自动启动 Master 所有 Worker): $SPARK_HOME/sbin/start-all.sh # 查看Master节点进程 Jps # 查看Worker节点进程(分别在hadoop103、hadoop104执行) Jps 解释:jps命令用于查看 Java 进程,需确保 hadoop102 有Master进程,hadoop103/104 有Worker进程,否则需重启集群:$SPARK_HOME/sbin/start-all.sh。 1.2验证HDFS服务可用性 词频统计需通过 HDFS 存储输入输出数据,执行以下命令检查 HDFS 状态: # 查看HDFS根目录 hdfs dfs -ls / # 测试HDFS写入权限 hdfs dfs -touchz /test.txt && hdfs dfs -rm /test.txt 解释:通过创建临时文件验证 HDFS 读写权限,若提示`Permission denied`需检查 Hadoop 目录权限配置。 2. 数据准备(HDFS 数据上传) 2.1 创建 HDFS 数据目录 # 创建输入目录(存放待分析文本) hdfs dfs -mkdir -p /spark-wordcount/input # 创建输出目录(后续运行前需删除,此处仅为演示) hdfs dfs -mkdir -p /spark-wordcount/output 2.2 准备本地测试文本 vi /home/atguigu/wordcount-data.txt 粘贴测试内容: Hello Spark Hello Hadoop Spark is fast Hadoop is stable Hello Spark HBase Spark Spark Spark 2.3上传文本到 HDFS hdfs dfs -put /home/atguigu/wordcount-data.txt /spark-wordcount/input/ # 验证上传结果 hdfs dfs -ls /spark-wordcount/input/ 3. 词频统计实现(Scala 语言方案) 3.1编写 Scala 代码 1. 创建代码目录(含 Maven 要求的源码结构) # 创建项目根目录 mkdir -p /home/atguigu/spark-code/scala # 进入项目根目录 cd /home/atguigu/spark-code/scala # 创建 Maven 规定的 Scala 源码目录(必须!否则 Maven 找不到代码) mkdir -p src/main/scala 2. 在正确目录下编写代码 # 注意:代码必须放在 src/main/scala 目录下 vi src/main/scala/WordCount.scala 3. 粘贴代码 // 导入Spark核心依赖(必须完整,避免编译错误) import org.apache.spark.{SparkConf, SparkContext} // 词频统计主类(单例对象,Scala中可作为入口类,类名必须与pom.xml的mainClass一致) object WordCount { def main(args: Array[String]): Unit = { // 1. 配置Spark应用(集群模式下,setMaster可通过spark-submit参数覆盖,更灵活) val conf = new SparkConf() .setAppName("Scala-WordCount") // 应用名称,Web UI可见 // 注:集群提交时建议删除setMaster,通过--master参数指定,避免硬编码 // .setMaster("spark://192.168.10.102:7077") .set("spark.executor.memory", "2g") .set("spark.cores.max", "4") // 2. 创建Spark上下文(核心入口,需确保配置正确) val sc = new SparkContext(conf) sc.setLogLevel("WARN") // 减少日志输出,聚焦结果 // 3. 处理输入输出路径(优先使用命令行参数,适配不同场景) val inputPath = if (args.length > 0) args(0) else "/spark-wordcount/input" val outputPath = if (args.length > 1) args(1) else "/spark-wordcount/output" // 4. 核心逻辑(分步注释,清晰易懂) val wordCounts = sc.textFile(inputPath) // 读取文件:每行作为一个元素 .flatMap(line => line.split("\\s+")) // 分词:按任意空白分割,扁平化为单词 .map(word => (word, 1)) // 标记计数:每个单词映射为(单词, 1) .reduceByKey(_ + _) // 聚合:按单词累加计数(简化写法,等价于(a,b)=>a+b) .sortBy(_._2, ascending = false) // 排序:按词频降序 // 5. 输出结果(控制台+HDFS,双重验证) println("=== 词频统计结果 ===") wordCounts.collect().foreach(println) // 控制台打印(仅适合小数据) wordCounts.saveAsTextFile(outputPath) // 保存到HDFS(分布式场景推荐) // 6. 释放资源(必须执行,避免内存泄漏) sc.stop() } } \- `textFile`:读取 HDFS 或本地文件,返回行级 RDD(弹性分布式数据集),是 Spark 数据处理的基础结构; `flatMap`:扁平化转换,将每行文本拆分为单个单词,解决`map`操作可能产生的嵌套数组问题; `reduceByKey`:Spark 核心聚合算子,自动按 key 分组并执行累加,比`groupByKey`更高效(减少网络传输); `collect`:动作算子,将分布式 RDD 数据拉取到 Driver 节点,仅适合小结果集查看。 4. 验证代码位置(关键检查): # 确保代码在 src/main/scala 目录下 ls -l src/main/scala/WordCount.scala # 预期输出:-rw-r--r-- 1 atguigu atguigu ... src/main/scala/WordCount.scala 3.2 打包 Scala 代码(Maven 方式) 1.创建 `pom.xml`: vi /home/atguigu/spark-code/scala/pom.xml 复制粘贴以下配置(适配 Spark 3.3.0 Scala 2.12) <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <!-- 项目基本信息(自定义,确保唯一即可) --> <groupId>com.atguigu.spark</groupId> <artifactId>spark-wordcount</artifactId> <version>1.0-SNAPSHOT</version> <!-- 版本管理(关键:Scala与Spark版本必须匹配) --> <properties> <scala.version>2.12.15</scala.version> <!-- Spark 3.3.0适配Scala 2.12.x --> <spark.version>3.3.0</spark.version> <!-- 与集群Spark版本一致 --> <maven.compiler.source>1.8</maven.compiler.source> <!-- 适配JDK 1.8 --> <maven.compiler.target>1.8</maven.compiler.target> </properties> <!-- 依赖配置(仅包含必要依赖,避免冲突) --> <dependencies> <!-- Spark核心依赖(scope=provided:集群已存在,打包不包含) --> <dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-core_2.12</artifactId> <!-- 后缀_2.12对应Scala版本 --> <version>${spark.version}</version> <scope>provided</scope> </dependency> </dependencies> <!-- 构建配置(核心:确保Scala代码编译并打包进JAR) --> <build> <sourceDirectory>src/main/scala</sourceDirectory> <!-- 指定Scala源码目录 --> <testSourceDirectory>src/main/test</testSourceDirectory> <plugins> <!-- 1. Scala编译插件(必须配置,否则Maven无法编译.scala文件) --> <plugin> <groupId>net.alchim31.maven</groupId> <artifactId>scala-maven-plugin</artifactId> <version>4.5.4</version> <executions> <execution> <id>scala-compile</id> <goals> <goal>compile</goal> <!-- 编译Scala源码 --> <goal>testCompile</goal> </goals> <configuration> <args> <arg>-dependencyfile</arg> <arg>${project.build.directory}/.scala_dependencies</arg> </args> </configuration> </execution> </executions> </plugin> <!-- 2. 打包插件(生成可执行JAR,包含主类信息) --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.3.0</version> <executions> <execution> <phase>package</phase> <goals><goal>shade</goal></goals> <configuration> <!-- 解决依赖冲突:保留最后一个依赖版本 --> <filters> <filter> <artifact>*:*</artifact> <excludes> <exclude>META-INF/*.SF</exclude> <exclude>META-INF/*.DSA</exclude> <exclude>META-INF/*.RSA</exclude> </excludes> </filter> </filters> <!-- 指定主类(必须与Scala代码中的object名称一致) --> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <mainClass>WordCount</mainClass> <!-- 无包名时直接写类名 --> </transformer> </transformers> </configuration> </execution> </executions> </plugin> </plugins> </build> </project> 编译并打包: # 进入项目根目录 cd /home/atguigu/spark-code/scala # 清理旧文件并编译(首次编译时间较长) mvn clean scala:compile # 【关键验证1】检查是否生成 .class 文件 ls -l target/classes/WordCount.class # 若输出 ".class" 文件,说明编译成功;否则检查代码语法或目录 # 打包 mvn package 3.3 提交任务到 Spark 集群 1. 上传 JAR 到 HDFS: hdfs dfs -put -f target/spark-wordcount-1.0-SNAPSHOT.jar /spark-jars/ 2. 删除旧输出目录(避免冲突): hdfs dfs -rm -r /spark-wordcount/output 3. 提交任务: $SPARK_HOME/bin/spark-submit \ --master spark://192.168.10.102:7077 \ --class WordCount \ hdfs://192.168.10.102:8020/spark-jars/spark-wordcount-1.0-SNAPSHOT.jar \ /spark-wordcount/input \ /spark-wordcount/output 4. 问题:版本过低需要升级 解决步骤:升级 Maven 到 3.3.9 及以上版本 步骤 1:查看当前 Maven 版本(确认问题) 执行以下命令,查看当前 Maven 版本: mvn -v 如果输出的`Maven home`对应的版本低于`3.3.9`(例如`3.0.5`),则需要升级。 步骤 2:下载并安装兼容的 Maven 版本 推荐安装Maven 3.6.3(兼容 3.3.9 + 要求,且稳定): 1. 进入临时目录下载安装包: cd /tmp wget http://maven.aliyun.com/nexus/content/groups/public/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.tar.gz 2. 解压到`/opt/module`目录(与你的其他软件目录统一): sudo tar -zxvf apache-maven-3.6.3-bin.tar.gz -C /opt/module/ 3. 重命名目录(方便后续配置): sudo mv /opt/module/apache-maven-3.6.3 /opt/module/maven-3.6.3 4. 配置环境变量(替换旧版本 Maven):编辑`/etc/profile`文件: sudo vi /etc/profile 添加以下内容(分别在hadoop102.103.104上进行配置)(确保覆盖系统默认的 Maven 路径): export MAVEN_HOME=/opt/module/maven-3.6.3 export PATH=$MAVEN_HOME/bin:$PATH 生效配置: source /etc/profile 步骤 3:验证 Maven 版本是否升级成功 再次执行: mvn -v 若输出类似以下内容,说明升级成功: ```plaintext Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f) Maven home: /opt/module/maven-3.6.3 Java version: 1.8.0_212, vendor: Oracle Corporation, runtime: /opt/module/jdk1.8.0_212/jre 根据以上内容,生成流程图,一共四部分分别是总体介绍,详细操作,问题解决总结与讨论
10-20
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值