day67 Scala 容器 隐式转换


I know, i know
地球另一端有你陪我




一、基本容器

Scala 中的容器又类似于 Python 中的容器
Tuple List Set Map
注意的是,四个基本容器全是不可变的

1、Tuple

Tuple 元组:不可变,有序,元素可以重复,最大长度为22

看样子感觉就不是拿来给使用者用的,更多是用在底层代码
二元元祖是一个类似 Map 中一个元素的东西(key,value)
package day67

object ScalaDemo13Tuple {

  def main(args: Array[String]): Unit = {


    /*
         Tuple 元组:不可变,有序,元素可以重复
         最大长度为22
     */

    // 定义
    val t1: Tuple1[Int] = Tuple1(1)
    val t2: (Int, Int) = Tuple2(1,2)

    // 取元素
    println(t2._1)
    println(t2._2)

    // 元祖主要用于参数特征的固定
    // (Int,Int)=>{......} 需要传入带有两个参数的函数
    // ((Int,Int))=>{......} 需要传入带有一个参数的函数
  }
}

2、List

List:有序的、不可变的(属于immutable包中),元素可以重复

注意 foreach map flatmap sort 等方法的返回类型,掌握的话对写 Spark 会很用帮助
package day67

import scala.collection.mutable

/*
      List:有序的、不可变的(属于immutable包中),元素可以重复
      如果将泛型指定为Any => 让元素为任意类型
 */

object ScalaDemo11List {

  def main(args: Array[String]): Unit = {

    // 定义一个List
    val list: List[Int] = List[Int](5, 3, 1, 4, 2)


    // 取元素
    println(list(0))
    println(list)

    // 常见的操作
    println(list.head) // 取第一个元素
    println(list.tail) // 取除第一个元素以外的元素
    println(list.max) // 取除最大的元素
    println(list.min) // 取除最小的元素
    println(list.size) // 返回list的大小
    println(list.length) // 返回list的长度
    println(list.take(3)) // 取3个元素
    println(list.sum) // 累加
    println(list.isEmpty) // 判断是否为空
    println(list.distinct) // 去重
    println(list.reverse) // 反转
    println(list.mkString(",")) // 按照指定的分割符拼接list中的每一个元素


    /*
          map
          接收一个函数f:参数类型为Int(list中每个元素的类型是Int)
          返回值类型由自己定义
          会将list中的每一个元素依次传递给函数f,最后返回一个新的list
     */

    val list2: List[Int] = list.map(i => i * i)
    // pow 返回的是 double 类型的
    val list3: List[Double] = list.map(Math.pow(_, 2))
    println(list2)
    println(list3)

    println("*" * 100)
    /*
          flatMap方法:
          接收一个函数f:参数类型为String(list中每个元素的类型是String)
          返回值类型为集合类的类型
          会将list中的每一个元素依次传递给函数f,最后返回一个新的list
          最后会将返回的"数据容器"展开,成一个 List
     */

    val words: List[String] = 
    		List[String]("fgh,123", "123,fgh,111", "fgh,sza,qyh")

    println(words)

    words.map(str => {
      val word: Array[String] = str.split(",")
      word.toList
    }).foreach(println)


    val word: List[String] = words.flatMap(str => {
      val word: mutable.ArrayOps[String] = str.split(",")
      word
    })


    words.flatMap(str => {
      val word: mutable.ArrayOps[String] = str.split(",")
      word
    }).foreach(println)

    println("*" * 100)


    /*
          foreach方法:
          接收一个函数f:参数类型为String(list中每个元素的类型是String)
          返回值类型为Unit
          会将list中的每一个元素依次传递给函数f,无返回值
     */
    words.foreach(println)

    println("*" * 100)


    /*
          groupBy方法:指定一个分组字段 按照什么进行分组
          最后返回一个Map(K-V格式) 会以指定的分组字段作为Key
          属于同一组的所有的元素(构造成List)作为Value

     */
    println(word.groupBy(a => a))
    // Map(qyh -> List(qyh), 123 -> List(123, 123),
    // fgh -> List(fgh, fgh, fgh),111 -> List(111),sza -> List(sza))


    /*

         filter方法:接收一个函数f:
         参数类型为Int(list中每个元素的类型是Int),返回值类型为Boolean类型
         会将list中的每一个元素依次传递给函数f,
         根据函数f返回的Boolean值进行过滤,为true即保留,为false即过滤

     */

    println(list)

    val newlist: List[Int] = list.filter(i => {
      var flag: Boolean = true
      if (i % 2 == 0)
        flag = false
      flag
    })
    println(newlist)

    // 简化写法
//    list.filter(i=>{i%2==1}).foreach(println)
    list.filter(_%2==1).foreach(print)


    /*
          sorted:对list按照从小到大的顺序进行排序 返回一个新的list
     */

    // 默认升序
    val sortedlist: List[Int] = list.sorted
    println(sortedlist)

    /*

          sortWith:
          接收一个函数f:参数有两个(分别是Int、Int类型)
          返回值类型为Boolean
     */

    // 这是一个降序,简单的方法是看箭头的形状
    list.sortWith( (a,b) => {
      a > b
    }).foreach(print)

    val tupleList: List[(Int, Int)] = List((1, 2), (3, 5), (2, 4))
    println(tupleList.sortWith((t1, t2) => {
      // 元祖取第n个元素的方法
      t1._2 > t2._2
    }))

    /*
          sortBy:指定一个字段进行排序
          接收一个函数f:参数是容器内元素的类型
          返回值类型自定
          根据函数f返回的值进行排序
     */

    println(tupleList.sortBy(t=>{t._2}))
	// println(tupleList.sortBy(_._2))
  }
}

3、Set

Set :不可变的、无序、元素唯一没有重复

package day67

import scala.collection.mutable

object ScalaDemo12Set {

  /*
    Set 集合:不可变的、无序、元素唯一没有重复
 */

  def main(args: Array[String]): Unit = {

    // 定义
    val set1: Set[Int] = Set[Int](1,2,2,3,4,6)
    println(set1)

    val set2: Set[Int] = Set[Int](1,2,3,4)
    val set3: Set[Int] = Set[Int](3,4,5,6)

    // 交集
    println(set2 & set3)
    // 并集
    println(set2 | set3)
    // 差集
    println(set2.diff(set3))

    // 可变的Set
    val hs: mutable.HashSet[Int] = mutable.HashSet[Int](1,2,3)
    hs.add(2)
    hs.add(4)
    hs.remove(2)

    hs.update(10,false)

    hs.+=(1,2,3)
    hs.-=(1,2,3)
  }
}

4、Map

Map :K-V格式,不可变的、无序的

package day67

import scala.collection.{GenTraversable, immutable, mutable}

object ScalaDemo14Map {

  def main(args: Array[String]): Unit = {

    /*
        Map :K-V格式
        不可变的、无序的
     */

    // Map 中的元素更像是二元元祖
    val map1: Map[Int, Int] = Map((1, 2), (2, 4), (3 -> 6))
    println(map1)

    // Map 中的遍历,注意此处的参数只有一个
    // 即 map 中的一个个二元元祖

    // 返回迭代器
    // 同List的区别在于:迭代器只能遍历一次 List可以遍历多次;
    // 迭代器中的数据不会直接加载到内存中,在需要的时候才会去取
    val iterator: immutable.Iterable[String] = map1.map(tup => {
      val key = tup._1
      val value = tup._2
      key + " + " + value
    })
    iterator.foreach(println)

    // 查找元素
    // 不推荐这种方法,因为可能会导致空指针异常
    println(map1(1))

    // 关于Scala如何处理该类问题
    // get方法返回值类型为:Option
    // Option 下面就两个子类:Some、None
    // 在Map中通过key获取value,对于使用者来说key到底存不存在是未知的
    // 所以get方法的返回值是不确定的 可能有 可能没有
    val v1: Option[Int] = map1.get(1)
    val v2: Option[Int] = map1.get(4)
    println(v1) // Some(2)
    println(v2) // None
    // 会发现即使是正确的key,也不能继续向下调用方法
    // 此时可以考虑使用模式匹配(case)或下面的方法
    println(map1.getOrElse(1, "key不存在"))
    println(map1.getOrElse(4, "key不存在"))

    // 判断key存不存在
    println(map1.contains(1))
    println(map1.contains(4))


    // 可变的Map
    val hm: mutable.Map[String, String] =
        mutable.HashMap[String,String](("k1","v1"),"k2"->"v2")

    // 这仨格式是固定的
    hm.put("k3","v3")
    hm.+=("k4"->"v4")
    hm.-=("k1")
    println(hm)


    // 尝试一个单词计数
    val words: List[String] = 
    List("java,scala,python","java,scala,python","java,scala,python")
    println(words)

    // 先进行一个 flatmap 展开
    // "java,scala,python"
    val word: List[String] = words.flatMap(_.split(","))
    println(word)

    // 分组
    val wordgroup: Map[String, List[String]] = word.groupBy(w=>w)
    println(wordgroup)

    // 一个客制化的概念
    // 元素是map中的每一个二元祖
    wordgroup.map(kv => {
      val word: String = kv._1
      val wordlist: List[String] = kv._2
      // List的长度,即单词的出现次数
      val count: Int = wordlist.length
      word +" : "+ count
    }).foreach(println)

    // 最后一步用模式匹配 case
    // 可以让过程更加清晰易读
    // map 不需要再传入参数
    wordgroup.map {
      case (word:String,wordlist:List[String]) => {
        word +" : "+ wordlist.length
      }
    }.foreach(println)


    // 再写一个 case
    // case 需要将每一个单元参数都写上
     val map2: Map[(String, String), (String, String)] = 
     		Map(("k1","v1")->("k2","v2"),("k3","v3")->("k4","v4"))
      map2.map{
        case ((k1:String,v1:String),(k2:String,v2:String)) =>{
          s"$k1,$v1->$k2,$v2"
        }
      }.foreach(println)
  }
}

二、隐式转换

给对象动态地增加方法
理解为 为了偷懒无所不用其极
分为三类:
隐式转换方法,隐式转换变量,隐式转换类

1、隐式转换方法

在参数不匹配时,会自动寻找是否存在类型转换相关的隐式转换方法进行调用

package day67

object ScalaDemoImplicit1 {
  def main(args: Array[String]): Unit = {
    // 隐式转换:给对象动态地增加方法

    /**
      * 分为三类:
      * 隐式转换方法
      * 隐式转换变量
      * 隐式转换类
      */

    // 1、隐式转换方法

    def func1(i: Int): Unit = {
      println(i + 100)
    }

    func1(10) // 110
    func1(100) // 200

    val str: String = "123"
    //    func1(str) // 类型不匹配 func1函数需要一个Int类型的参数
    func1(Integer.parseInt(str)) // 手动的将 str => Int 类型

    // 隐式转换方法
    implicit def str2int(str: String): Int = {
      println("调用了隐式转换方法")
      Integer.parseInt(str)
    }

    // 同一个作用域范围内只能存在一个同类型(函数的参数和函数的返回值类型相同)的函数
    //    implicit def str2int2(str: String): Int = {
    //      Integer.parseInt(str) + 1
    //    }

    func1(str2int(str)) // 使用自定义的str2int方法 将str => Int 类型
    // 可以省略str2int方法调用过程
    // str2int是一个隐式转换方法
    // 虽然func1需要Int类型的参数,这里传入了String类型的变量
    // 但是最后会自动寻找一个隐式转换函数(可以将String类型转换为Int的一个函数)
    func1(str)

  }

}

2、隐式转换变量

类似默认参数

package day67

object ScalaDemoImplicit2 {
  def main(args: Array[String]): Unit = {
    // 2、隐式转换变量

    def addSuffix(head: String)(implicit tail: String): Unit = {
      println(head + ":" + tail)
    }

    // 调用方法
    addSuffix("Hadoop")("大象")
    addSuffix("Hive")("蜂巢")
    addSuffix("Spark")("火花")

    // 隐式转换变量
    implicit val default_tail: String = "默认的tail"

    // addSuffix方法需要调用两次
    // 在这里会自动去寻找跟第二次调用需要的参数tail同类型的一个隐式转换变量 然后自动进行调用
    // 简化的函数的调用
    addSuffix("HBase")
  }
}

3、隐式转换类

让成员属性同类型的变量可以直接调用成员方法,代价是只能存在一个属性

package day67

import scala.io.{BufferedSource, Source}

object ScalaDemoImplicit3 {

  def main(args: Array[String]): Unit = {
    // 3、隐式转换类
    val path: String = "Scala/data/words.txt"
    // 正常的使用方法
    val rf1: ReadFile = new ReadFile(path)
    rf1.read_file()
    println(rf1.lines)

    // 隐式转换下的使用方法
    val lines: List[String] = "Scala/data/students.txt".read_file()

    "Scala/data/students.txt".printR()


  }

  /**
    * 隐式转换类
    * 动态地给 与ReadFile类所需要地构造方法的参数path 同类型的变量
    * 添加ReadFile类所有的方法
    * 默认的构造方法只能接收一个参数
    */
  implicit class ReadFile(path: String) {
    var lines: List[String] = _

    def read_file(): List[String] = {
      val br: BufferedSource = Source.fromFile(path)
      lines = br.getLines().toList
      br.close()
      lines
    }

    def printR(): Unit = {
      println("ReadFile中的printR方法")
    }
  }
}

各种小零散碎


1、模式匹配(case)

并不样例类中的 case
类似 Java 中的 swith case 结构,可以匹配不同的结构、类型等等
做出不同的应对

package day67

object ScalaDemo15Case {
  def main(args: Array[String]): Unit = {
    /**
      * 模式匹配 相当于switch
      * java中可以匹配:基本数据类型、字符串、枚举
      * scala中可以匹配:基本数据类型、字符串、枚举、类型、样例类对象
      */

    // 1、匹配基本数据类型
    val i: Int = 10

    i match {
      case 1 => println("i是1")
      case 10 => println("i是10")
      case _ => println("其他情况")
    }


    // 2、匹配字符串
    val str: String = "efg"
    
    str match {
      case "abc" => println(str.reverse)

      case "efg" => println(str)

      case _ => println("其他情况不做任何处理")
    }


    // 3、匹配类型
    val j: Any = "100"
    
    j match {
      case t1: Int => println("j是Int类型,j的值为" + t1)
      
      case t1: String => println("j是String类型,j的值为" + t1)
      
      case _ => println("j是其他类型")
    }

    val map: Map[String, String] = Map("k1" -> "v1", "k2" -> "v2")
    val get: Option[String] = map.get("kk1")

    // 模式匹配在Map中的应用
    // 模式匹配可以有返回值
    get match {
      case Some(v) => println("key是存在的,对应的value值为 " + v)

      case None => println("key不存在,返回了None,使用时要小心")
    }


    // 4、匹配样例类
    case class Stu(id: String, name: String)

    val stu: Stu = Stu("001", "张三")

    stu match {
      case Stu("001", "张三") =>  println("匹配到了学生张三")

      case Stu("002", "李四") => println("匹配到了学生李四")

      case _ => println("其他学生")
    }
    
  }
}

2、Null null Nil Nothing None Unit

package day67

import day65.B

object ScalaDemo16NoneNilNullNothingUnit {
  // Null null Nil Nothing None Unit
  def main(args: Array[String]): Unit = {
    // 1、Null and null
    // Null是一个trait 类似抽象类和接口的融合
    // null是Null的实例
    def tryit(thing: Null): Unit = {
      println("That worked!")
    }
    //  tryit("hey")
    val someRef: String = null
    //  tryit(someRef)

    tryit(null)

    val nullRef: Null = null
    tryit(nullRef)


    // 2、Nil 空的列表
    val nil: Nil.type = Nil
    println(nil)
    println(nil.length)


    // 3、Nothing
    /**
      * trait :scala中的接口 但又像抽象类
      * Nothing是另一个trait。它继承了Any类。Any是整个Scala类型系统的超类。
      * Any可以引用对象类型以及诸如普通的旧整数或双精度的值。
      * Nothing是一切类的子类。
      * Nothing没有具体的实例。
      */

    val emptyStringList: List[String] = List[Nothing]()

    val f:List[B] = List[Nothing]()

    val emptyIntList: List[Int] = List[Nothing]()

    // 没有具体的实例
    // al emptyStringList: List[String] = List[Nothing]("abc")


    // 4、None

    /**
      * 当你写一个函数,遇到没有什么实用价值的情况时,你会怎么做?
      * 通常有几种方法来处理它:
      * 1、你可以返回null,但是这会导致问题。
      * 如果调用者不希望得到null,
      * 那么当他尝试使用它时可能会面临NullPointerException
      * 否则调用者必须检查null
      * 一些函数不会返回null,但有些函数可能会。
      * 作为一个调用者,他不知道那些函数会返回null 哪些不会。
      *
      * 2、使用throws关键字,作为异常抛出。
      * 需要用try / catch处理
      * 通常希望在真正发生异常的情况下才使用try/catch
      * 而不仅仅是为了处理一个普通的无结果的情况
      *
      * 3、Scala中的方式。
      * 如果你想返回一个字符串
      * 但你知道你可能无法返回一个合理的值
      * 你可以返回一个Option[String]
      */

    val s:String = null
    if(s!=null){
      s.split(",")
    }

    def getAStringMaybe(num: Int): Option[String] = {
      if (num >= 0) {
        Some("A positive number!")
      }
      else {
        None
      }
    }

    val num: Int = 100
    getAStringMaybe(num) match {
      case Some(str) => println(str)
      case None => println("No string!")
    }

    // 5、Unit 相当于Java中的void,表示函数不返回任何值
  }
}

3、Trait

翻译过来是特征的意思
类似 Java 中接口和抽象类的私生子
可以存在抽象方法和普通方法,常量和变量
可以多继承

package day67

// Scala中的特征 相对于Java中的接口
trait Demo17Trait {
  val i: Int = 10

  // 定义了抽象的方法
  def isEqual(i: Int): Boolean

  // 对方法进行了具体的实现
  def isNotEqual(i: Int): Boolean = {
    !isEqual(i)
  }

}

class P(id: String) {
  val _id: String = id
}

// trait类可以一次继承多个,用with连接
class F(id: String) extends P(id) with Demo17Trait {
  override def isEqual(i: Int): Boolean = {
    id.toInt == i
  }
}

object Demo17Obj {
  def main(args: Array[String]): Unit = {
    val f = new F("18")
    println(f.i)
    println(f._id)
    println(f.isEqual(18))
    println(f.isNotEqual(18))
    println(f.isEqual(20))
    println(f.isNotEqual(20))
  }
}

零碎

Tuple List Set Map 四个基本容器全是不可变的

HashSet 可以理解成一个没有 value 的HashMap
其底层是一个默认长度为 16 的数组,和一个链表,数组中每个元素对应一条链表

会计算元素或者 key 的HashCode,再对 16 取余,添加至对应数组位的链表中
当链表的长度超出一定长度后,会被优化成红黑树

红黑树是一个自平衡二叉搜索树,其本质是一种对4阶B树的实现
其红色节点的特殊含义即是对3节点和4节点的实现

LinkedHashSet 和 LinkedHashMap 相较于上面,
是在各个元素之间多维护了一条双向链表,保证了遍历的时候有序

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值