scala学习笔记

元组

元组是一个轻量级集合,这个集合可以存储任意元素

元组是使用小括号,元素之间使用逗号分隔,元素的类型是任意的

若需要访问元组中的元素 [元组名._数字] 数字是从1开始, 有多少个元素,这个1递增多少次

例如: 有一个元组,元组中有三个元素

  访问每一个元素值 --> _1 _2 _3

元组属于轻量级的集合,数据最多只能存储22个

ps:一般元组会代替Map在Spark中使用

对偶元组 --> 元组中存储的数据是成对出现的,就将出现第一个值称为key,第二个值的位置称为Value

/**
  * 元组
  */
object TupleDemo {
  def main(args: Array[String]): Unit = {
    //这个就是元组的创建
    val t = ("sparkcore","sparkSQL","SparkStreaming")
    //获取值
    val value: String = t._1

    //创建元组的同时,指定变量存储数据(不推荐)
    val t1,(a,b,c) = ("sparkcore","sparkSQL","SparkStreaming")
    //
    val t1_1 = a
    
    //Scala中提供通过tuple元组类来创建
    //tuple1是无限大 
    //剩余的tuple后面的数字代表了最多可以存储多少个元素
     //系统给定元组的最大值是22个,超过22就无法存储了,此时建议改为Array或List
    val tuple = new Tuple1(1.3,14,"你好")
    val tuple2 = new Tuple2(1.3,14)
  }

  

元组的遍历

val tuple = new Tuple1(1.3,14,"你好")
    val tuple2 = new Tuple2(1.3,14)

     //遍历方式1(迭代器遍历)
      for(ele <- tuple.productIterator){
        println(ele)
      }
    //遍历集合 --> foreach 这个方法就是遍历集合使用,没有返回值
    //foreach方法是获取集合每一个元素并处理, 返回值是一个泛型,所以最终输出什么数据类型由需求决定
    /*
    方法:
      def 方法名(参数列表):返回值类型 = { 方法体 }
    函数:
      val 函数名 = (参数列表) => {函数体}
     */
    //先定义一个函数
     val pt = (x:Any) =>{println(x)}
    tuple.productIterator.foreach(pt)

    //匿名函数
    tuple.productIterator.foreach((x:Any) =>{println(x)})
    //简化
    tuple.productIterator.foreach(x =>println(x))
    //只需要一个可以获取数据的变量--> 此时可以使用 下划线代替
    tuple.productIterator.foreach(println(_))
    //因为是打印数据,此时参数是默认(就是遍历集合获取来的,此时Scala允许再次省略下划线)
    tuple.productIterator.foreach(println)

  

拉链操作

作用:将两个元组中的数据形成对偶元组

package Scala_03

/**
  * 拉链操作
  */
object TupleDemo2 {
  def main(args: Array[String]): Unit = {
      //zip就是元组的拉链操作,将两个元组中数据进行合并,形成对偶元组
      val name = Array("tom","jerry","kitty")
      val scores = Array(2,3,4)
    //当前数组中存储的是一个一个的元组对象
    //谁调用方法就是就是key,谁做参数传递 谁就是 value
    val tuples: Array[(String, Int)] = name.zip(scores)
    println(tuples.toBuffer)

    //两个存储数据的集合 中的数据不一致
    val name_1 = Array("tom","jerry","kitty")
    val scores_2 = Array(2,3)
    //ps:如果两个集合数据长度不一致,此时拉链操作谁以最小长度的集合为基准 进行对偶元组合并,多余的部分会删除
    val tuples_2: Array[(String, Int)] = name_1.zip(scores_2)
    println(tuples_2.toBuffer)

    //zipAll 和zip是类似的,但是若齐总某一个集合集合中的元素缺少,将使用默认元素填充
    val xs = List(1,2,3)
    val zx = List("一","二","三","四")
    val tuples_3 = xs.zipAll(zx,0,'_')
    println(tuples_3)

    //zipwithIndex 将集合中元素和所以进行结合
    val list = List(1,2,3)
    val tuples_4 = list.zipWithIndex
    println(tuples_4.toBuffer)
    //若需要从指定位置开始组合
    val tuples_5 = list.zip(Stream from 1)
    

  }
}

  

列表List

Scala中的集合是分为可变和不可变

Scala中默认使用是不可变集合包 scala.collection.immutable

package Scala_03

/**
  * list集合
  */
object ListDemo {
  def main(args: Array[String]): Unit = {
    val empty = List()  //空集合
    val names = List("xiaobai","xiaohong","xiaohuang") //有值
    val moreList = List(List(1,2,3),List(2,3,4))// 集合中存储的数据是另外一个集合对象
    //运算符
    //列表中有一个默认空值 nil 它可以和 :: 中缀符 一起使用 构建集合对象
    // :: 和 nil一起使用 它 是右结合
    val str:List[String] = ("小明" :: ("小红" :: ("小黄" :: Nil)))
    println(str)
    val s = List("姓名")
    //将其他集合中数据添加到当前集合汇总
     val strs :List[String] = "小明" :: "小红" :: "小黄" :: s
     println(strs)
   //使用nil之后会将当前集合先看做是一个整体 nil就相当于创建了集合,所以当前所有数据 都会当前集合中元素进行存储
     // s 就会当做一个List集合对象存储到当前集合中
    val ss = "小明" :: "小红" :: "小黄" :: s :: Nil
    println(ss)
   
  }
}

  

列表添加数据

package Scala_03

object ListDemo2 {
  def main(args: Array[String]): Unit = {
      val  list = List(1,2,3)
     //所有不可变list操作都会返回一个全新的List
     val list2 =  0 :: list  //     添加数据
    val list3 = 0 +: list   //都是向集合的开头添加数据
    val list4  = list :+ 4 //向集合的末尾提N家数据
    val list5 = list ++ list4 //将两个集合合并
    //将某一个集合添加到当前集合的前面
   val list6 = list4 ++: list
    val list6_1  = list4 ::: list //此操作和上面的操作是一样的
    
  }
}

  

列表的常用操作方法

package Scala_03

object ListDemo3 {
  def main(args: Array[String]): Unit = {
    //遍历集合的方法:
    /*
     1.map遍历集合获取集合中每一个元素,并将元素进行处理(自定函数函数),返回一个全新的集合
     2.filter遍历集合获取集合每一个元素,并将元素进行处理(这个自定义函数返回值必须是Boolean)
     然后只有得到true,才会存储到新的集合中
     这两个方法同样适用于List
     */
    //3.foreach 这个方法主要作用即使遍历集合并处理集合中数据(自定义函数),这个方法没有返回值
    val list = List(1,2,3)
    val list_1: List[Int] = list.map(_*2)
    val list_2: List[Int] = list.filter(_>1)
    list.foreach((x:Int)=>println(x))
    list.foreach(x => println(x))
    list.foreach(println(_))
    list.foreach(println)
    /*
    map和foreach的区别
    1.map方法存在返回值,处理完数据之后会返回一个新的集合但是foreache是没有返回值的,处理完数据之后时没有返回结果的
    2.因为map具有返回值,所以map多用于进行集合中数据处理,而foreach没有返回值,所以多用于打印集合集合中数据
     */
    //ps:s虽然foreach不具备返回值,但是foreach内部处理函数和Map其实是一样的,所以foreach也可以对集合中数据操作,操作完成之后多用于输出
    //例如 map将集合集合中的元素扩大二倍,foreach也可做到,但是不能返回值出新的集合
      list.foreach(x=>println(x*2))
     // list.max  最大值  list.min 最小值

    //head 返回列表第一个元素
      val head: Int = list.head
    //tail 返回除第一个元素之外的其他元素
      val tail: List[Int] = list.tail
    //concat 将两个集合拼接 相当于是 ++
      val list1 = List(4,5,6)
    val newList: List[Int] = List.concat(list,list1)
    //反转
     val reverse: List[Int] = list.reverse
    //长度
    //list.length    list.size
    //take -> TopN   提取列表前N个数据 --> N就是具体的数值
      val topN: List[Int] = list take 1
    //拉链 zip  zipAll  zipwithIndex都可以使用
    //list.sorted  list.sortwith 都可以使用排序
     //丢弃列表前N数据 --> N是具体的值

      val ints: List[Int] = list drop 1
  }
}

  

可变list

package Scala_03
/*
可变ListBuffer
 */
object ListBufferDemo {
  def main(args: Array[String]): Unit = {
      import scala.collection.mutable.ListBuffer
      val list1 = ListBuffer[Int](1,2,3)
      val list2 = new  ListBuffer[Int]
      list2 += 4  //追加
      list2.append(1)
      list2 ++= list1 //添加集合
    
  }
}

对列表进行拆分
val list = List(1,2,3)
val List(a,b,c) = list
此时 a b 和c 就会获取 1,2,3的值

  

Set

建议使用HashSet

package Scala_03

import scala.collection.immutable.HashSet
import scala.collection.mutable

/*
   不可变和可变 HashSet是排重
   Hash表 --> 数组+链表
 */
object HashSetDemo {
  def main(args: Array[String]): Unit = {
         val set = new HashSet[Int]()
         val set1: HashSet[Int] = set+1 //添加
         val set2 = set1 ++ Set(1,2,3)//合并集合
        println(set2)
      //除了HashSet之外 可以使用使用Set
       val s = Set(1,2,3)
      
        import  scala.collection.mutable._
        val mutableSet = Set(1,2,3)//可变Set
        val set3 = new mutable.HashSet[Int]()
         set3 += 2
         set3.add(4)
    //存在则删除,不存在 不会报错不生效
         set3  -= 2
         set3.remove(2)
  }
}

  

两个Set集合操作

package Scala_03

/**
  * 两个Set集合操作
  */
object SetDemo2 {
  def main(args: Array[String]): Unit = {
      //合并Set集合
      val set1 = Set(5,6,9,20,30,45)
      val set2 = Set(50,60,9,20,35,55)
       val set  = set1 ++ set2
    //交集
      println(set1.&(set2))
    println(set1.intersect(set2))
    //并集
     println(set1.union(set2))
    //差集
     println(set1.diff(set2))
    //遍历Set
      val iterator = set1.iterator
     while(iterator.hasNext){
       println(iterator.next)
     }
    
  }
}

  

总结:

Scala中集合:分为可变和不可变

List Set Array Map -->可变和不可变,Array是定长和变长

元组轻量级存储数据的一个集合,最多只能存储22个元素多用于是对偶元组代替Map

在Spark中使用级别: 元组->Array-->List--->Map---->Set

整个集合记住基本操作 添加数据和创建,集合所有的常用方法必须记住

集合常用方法

ps:方法主要是以Array和List为主,其余Map和Set可以使用个别方法
遍历集合:
map 遍历操作数据并返回
filter遍历操作数据并返回 满足Boolean表达式
foreach  遍历操作数据但是没有返回多用于打印

package Scala_03

/**
  * 常用方法
  */
object CollectionMethodDemo {
  def main(args: Array[String]): Unit = {
        val list = List(List(1,2,3),List(4,5,6))
    //要求:  将集合进行处理 并得到 一个 新的集合 List(1,2,3,4,5,6)
      //压平集合(可以将集合中存储的集合进行扁平化处理)
     //将集合存储的集合对象进行扁平化处理(将存储在集合对象中集合数据获取出来形成一个新的集合)
     val flatten: List[Int] = list.flatten
      println(flatten)

      val line = List("hello tom hello jerry","hello xiaobai hello")
    //需求将当前集合中的数据进行处理  -- >  List("hello","tom","hello".....)
       val stringses: List[Array[String]] = line.map(_.split(" "))
       stringses.foreach(x=>println(x.toBuffer))
        val list_1 = stringses.flatten
       println(list_1)

     //Scala中为了应对数据中存在集合套用集合的情况,推出 faltMap --> 就是flatten + Map  遍历的同时并压平
     val line_1 = List("hello tom hello jerry","hello xiaobai hello")
    val strings: List[String] = line_1.flatMap(_.split(" "))
    /*
    flatMap和Map的区别
    都可以对数据继续进行遍历处理,.map只是对数据处理并不具备对数据的扁平化处理,flatmap是Flatten+Map结合体,即可以遍历数据也可以对数据进行
      扁平化处理,flatMap是Spark中比较常用获取数据后的处理方法,其次是Map,但是flatMap是有局限性,在于它会压平数据,不是所有的情况都需要压平.
     */

    //forall 对整个集合中元素进行条件过滤.只有当所有元素都满足的时候,才会返回true 否则就是false
       val  list_2 = List(1,2,3,4,5)
    val bool: Boolean = list_2.forall( _ < 3)

    //partition  分区 主要是对集合集合汇总数据进行分区(分组)
    //scala中体现就是出现不同集合, 但是在SparkCore中是存在不同分区中[Sparkcore中体现]
    val  list_3 = List(1,2,3,4,5,6)
    //这boolean类型表达式决定如何分数据
      val tuple: (List[Int], List[Int]) = list_3.partition(_%2 == 0)

    //fold 和  reduce
    //fold是一个聚合函数(求和) 需要两个参数  第一个是默认值  第二个参数是计算逻辑
    //计算流程: 首先x会先获取0这个初始值,然后y会获取集合第一个元素然后相加
    //          之后的每一次计算 x获取的都是上一次计算的结果 即 x  = x+y
    //           y值依旧获取集合中剩余的元素进行计算 ,最终停止是是y不能再获取到集合元素时停止
    val  list_4 = List(1,2,3,4,5)
      val sum = list_4.fold(0)((x,y) => x+y)
    
    //reduce  
    //计算流程 首先x和y会获取集合中第一个或第二个元素的值并进行计算 --> 1+2 = 3
     //       之后的每一次计算x获取的是上一次计算的结果即 x = x+y  而y继续获取剩余的集合中的元素参与运算 直到集合中没有元素停止
    list_4.reduce((x,y)=>x+y)


  }
}

求和 sum 
排序 sorted , sortwith
最大值 max  和 最小值 min

  

Scala版本的WordCount

 

package Scala_03
/*
Scala版本  模仿做的
 */
object WordCount {
  def main(args: Array[String]): Unit = {
    // 读取文件数据存储在集合中
    val line = List("hello tom hello jerry","hello xiaobai hello","hello tom")
    //将集合中的数据进行处理,,获取集合中每一条字符串,将字符串进行拆分得到单词
     val words: List[String] = line.flatMap(_.split(" "))
    //需要将单词以KV的形式记性拼接  -->  (单词,1) --> 明确当前存储类型就是kv键值对
    // Scala中提供两种方式 Map 和 元组 --> 推荐使用元组,原因在于元组擦操作数据更加方便,不会像Map一样需要调用get方法获取数据
    val tuples: List[(String, Int)] = words.map((_,1))
    //单词和次数已经成为了元组,需要对次数进行累加,问题是不知道哪些单词是一组
    //  将相同的单词进行分组
    //            key 单词   value 原有单词所存在的元组
    val grouped: Map[String, List[(String, Int)]] = tuples.groupBy(_._1)
      //此时已经根据单词进行了分组 相同的单词会汇聚成List集合,通过reduce来进行计算可以相对来说比较麻烦
      val sumed:Map[String,Int] = grouped.mapValues(_.size)
    //Top1 ,数据已经存在Map集合中,此时进行排序
       val list: List[(String, Int)] = sumed.toList  //Map转换为List时会将kv键值对以元组的形式存
    //这个排序是根据传入的指定值来进行排序的,默认是升序,无法直接降序,Spark中有一个和这个方法一样的sortBy,是可以自由升序降序
      val sortedList = list.sortBy(_._2)
       val reverseList = sortedList.reverse
        val top1 = reverseList take 1
    println(top1)

    //正常版本
    val top2 = line.flatMap(_.split(" ")).map((_,1)).groupBy(_._1).mapValues(_.size).toList.sortBy(_._2).reverse.take(1)
 

  }
}

  

并行化par

原因:Spark是一个并行计算框架,基于内存,所以会开启多个线程执行程序
Scala中可以使用par进行多线程模拟
package Scala_03

/**
  * 并行化
  */
object ParDemo {
  def main(args: Array[String]): Unit = {
      /*
      Spark在编写程序的时候,可以提交集群运行,也可以在本地执行
      可以在编写Spark程序的时候 进行本地运行配置setMaster 运行模式设置
      需要开启本地运行--> 我们三种方式
      local   --> 单线程
      local[值]  --> 更值来开启对应的线程数,用来模拟spark的平行计算
      local[*]  --> * 主要代表当前电脑空闲多少线程就使用多少线程来进行模拟计算
       */
      //用par模拟并行化计算
      //1.求和 -->聚合函数  --> sum , fold  ,reduce
      //1.1 sum进行计算
      println("--------------------------------sum计算------------------------------------")
      //ps:sum只能用来求和无法计算其他方式
      val arr = List(1,2,3,4,5,6,7,8,9,10) //55
      //单线程计算
       val sumed = arr.sum
       println("单线程sum:"+sumed)  // 1+2+3+4...
      //开启平行化处理计算
       val sumed2 = arr.par.sum
      println("多线程sum:"+sumed)  //(1+2)+(3+4+5+6)+(7+8+9+10)
      //总结:无论是在单线程还是多线程计算求和计算结果都是没有问题
      println("--------------------------------sum计算------------------------------------")

      //reduce 求和
    println("--------------------------------reduce计算------------------------------------")
    //ps:sum只能用来求和无法计算其他方式
    val arr_1= List(1,2,3,4,5,6,7,8,9,10) //55
    //单线程计算
    val sumed3 = arr_1.reduce(_+_)
    println("单线程reduce:"+sumed3)  // 1+2+3+4...
    //开启平行化处理计算
    val sumed3_1 = arr_1.par.reduce(_+_)
    println("多线程reduce:"+sumed3_1)  //(1+2)+(3+4+5+6)+(7+8+9+10)
    //总结:无论是在单线程还是多线程计算求和计算结果都是没有问题

    //reduce是可以写自己的计算逻辑,既然可以使用+  同理也可以计算 -
    //单线程计算
    val sub = arr_1.reduce(_-_)   // 1-2-3-4...
    println("单线程reduce:"+sub)
    //开启平行化处理计算  此时不知道有多少个线程在线程在执行(1-2)  -(3-4-5)  -  (9-10)
    val sub_1 = arr_1.par.reduce(_-_)
    println("多线程reduce:"+sub_1)
    //在计算求差值计算时候reduce是无法保证在多线程下计算记过准确
    // reduceleft  和 reduceright  --> 无论是单线程还是多线程 ->都是按照一个方向计算计算的
    //可以使用reduceleft来修正计算计算记过 --> 必须是从左向右计算
    val sub_2 = arr_1.par.reduceLeft(_-_)
    println("多线程reduce:"+sub_2)
    //在Spark并行计算中如果需要使用计算求差值这样的计算,建议使用 reduceLeft   --> reduceRight计算逻辑和left是一样的(从右向左)
    println("--------------------------------reduce计算------------------------------------")

    //fold 求和
    println("--------------------------------fold计算------------------------------------")
    //ps:sum只能用来求和无法计算其他方式
    val arr_2= List(1,2,3,4,5,6,7,8,9,10) //55
    //单线程计算
    val sumed4 = arr_2.fold(10)(_+_)
    println("单线程fold:"+sumed4)  // 1+2+3+4...
    //开启平行化处理计算
    val sumed4_1 = arr_2.par.fold(10)(_+_)  //fold一旦开启并行化,就会进行多次的计算--> 当前整个初始值只要进行进行一次线程计算
    //就会尽心一次10的相加 --> 例如: 10+1+2 --> 12   10+3+4
    println("多线程fold:"+sumed4_1)
     //总结在平行化的前提下,fold 不建议进行 求和计算,因为会多次计算初始值,如果需要当前初始值只计算一次
    //foldLeft 和 foldRight  --> 强制要求计算方式是从左向后  和 从右向左
    val sum5 = arr_2.par.foldLeft(10)(_+_)

     
    //聚合函数    aggregate 和 fold类似 但是 计数逻辑多
     //  aggregate(初始值)(局部聚合,全局聚合)  --> aggregateByKey(SparkCore中算子)

    println("--------------------------------fold计算------------------------------------")
  }
}

  

面向对象

Scala语言和Java语言,世间万物皆对象,Scala也是面向对象的语言,Scala的面向对象和Java的面向对象本质概念一样,语法时区别

Java的面向对象Scala中面向对象
创建一个Java类只能使用class 创建一个类可以使用class和object
Java中有丰富权限修饰符,每个权限修饰符都可以很好限制对类和勒种属性,方法的访问方式 Scala中权限修饰符本质只有两个一个就是public和另外一个private,默认使用修饰符是public
Java中类的构造方法需要写在类的内部,以方法的实行出现,并且主要是为了对属性进行赋值 Scala中构造方法有两种,一种是主构造器(一个类中只能有一个),另外一个是辅助构造器(一个类中可以有多个)
Java中是有抽象类,抽象类中可以定义抽象方法但是必须使用abstract Scala中也有抽象类,Scala中也存在抽象方但是不需要使用abstract,Scala允许抽象属性
Java中类比较单一就是class修饰普通类,只不过功能上会进行区分(描述类和执行类) Scala中提供一个概念,伴生类和伴生对象(在同一个文件中类名相同,分别使用class和object修饰)
Java中接口分为两种,一种是Java8之前的这个接口只能创建属性和抽象方法,另外一种中是Java8之后这个接口即可以定义属性和抽象方法也可以第定义实现方法(default修饰 和 static修饰) Scala中特质Trait本质上和Java中接口是没有区别(应对的是Java8以后接口),允许定义属性和抽象方法并且可以提供实现的方法
 
   

Scala中定义类和属性

ps:在scala中描述一个事物需要使用class修饰的,在Scala中需要执行代码即执行类需要使用object来修饰

Scala中 object代表的是类的修饰,而Java中Object代表的是超级父类,Scala中的超级父类是Any

package Scala_03
//Scala类中属性
/*
 在Scala中类并不需要使用public修饰,因为默认都是共有
 在Scala中class修饰的类都是用来描述事物的,在没有指定构造发方法之前,默认是存在一个无参构造方法的
 */
class Student {
    //类中定义属性
    /*
      因为Scala中对变量的修饰基本上即使var 和 val,这样两种修饰同样适用于属性
      定义属性时使用var修饰,那么就相当于属性自带getter和setter
      定义属性时使用val修饰,那么就相当于属性自带getter
       Scala类声明属性,如果使用自动推断,必须显示的进行赋值
                         不想使用自动推断,此时必须明确表示该数据类型是什么
     */
     var name  = "tom"
     var age:Int = _  //当前属性并不需要初始值,此时可以使用 _ 作为占位符代替初始值
     //val修饰属性不可以使用_ 占位因为是只读属性 只会被赋值一次,一但使用下划线 就无法在赋新值
     //  val gender:String= _
     //没有明确数据类型,此时不允许使用下划线
     //var  address = _
     // 可以使用 私有修饰符 (伴生类可以访问这个私有属性)
     private  var height :Int  = 155
     
     //对属性更加严格的权限控制 [this]代表只能当前类访问 伴生类也不可以
    private[this] val cardID = "123456"
  
     //创建对象
     val student = new  Student()
      
  // val student = Student//这样的创建多用于是伴生类 --> 触发 apply方法  Student必须是一个Object类
  
}

  

自定义getter和setter方法

package Scala_03
/*
自定义getter和setter
 */
class Ponit {
      /*
         为了保证数据安全,面向对象中封装性
         会将属性进行私有化设置 做私有化属性时建议的命名规则 要求以 _ 为前缀, 定义属性的名字 例如 _属性名
         getter方法  def 方法名 =  当前属性   --> 方法名是当前属性的名字
         setter方法  def 方法名(参数列表):Unit = { 对属性赋值}  --> 方法名必须是  属性名_
       */
     private var  _x = 0
    //获取这个属性  getter
    def  x = _x
   //赋值  setter
    def  x_(x:Int):Unit = {
        _x = x
    }
     def  showInfos()={
         val p = new  Ponit()
         //获取属性
         println(p.x)
         p.x_(10)
     }

}

  

Bean属性

为了方便从Java程序猿转变为Scala程序猿,方法理解和使用和Java中getter和Setter完全一样

ps:这种方式不能私有化

package Scala_03

import  scala.beans.BeanProperty
class Person {
   //在创建属性时候提供getter和setter方法,使用是一个注解
    @BeanProperty var name:String = _
}
object Person{
  def main(args: Array[String]): Unit = {
      val p = new Person()
     p.getName
      p.setName("1")
  }
}

  

Scala中集合和Java中集合互相转换

package Scala_03

import java.util

import scala.collection.mutable

/**
  * 集合互相转换
  */
object ScalaToJava {
  def main(args: Array[String]): Unit = {
    import  scala.collection.JavaConverters._
    //将当前集合转换为Java中的List
          val javaC: util.List[Int] = List(1,2,3,4).asJava
    //Java中集合转换为Scala中集合
        val scalaC: mutable.Buffer[Int] = javaC.asScala
      //万能方法 toXXXX -> XX就是你要转换的集合
       scalaC.toList
    
  }
}

  

posted @ 2019-12-05 21:18  梁衍  阅读(414)  评论(0编辑  收藏  举报