Scala 实战

栏目: Scala · 发布时间: 7年前

内容简介:详见,《在不同场景下,Tips: Full code is
List(1, 9, 2, 4, 5) span (_ < 3)       // (List(1), List(9, 2, 4, 5)),碰到不符合就结束

List(1, 9, 2, 4, 5) partition (_ < 3)  // (List(1, 2), List(9, 4, 5)),扫描所有

List(1, 9, 2, 4, 5) splitAt 2          // (List(1, 9),List(2, 4, 5)),以下标为分割点

List(1, 9, 2, 4, 5) groupBy (5 < _)    // Map(false -> List(1, 2, 4, 5), true -> List(9)),分割成 Map 对象,以 Boolean 类型为 Key

Iterator

grouped

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.sql.SparkSession
import org.apache.spark.sql.BigquerySparkSession._
val conf = new SparkConf()
val builder = SparkSession.builder().config(conf).enableHiveSupport()
val spark = builder.getOrCreateBigquerySparkSession()
val df = spark.sql("use db; select * from table")

val dataset = df.rdd.mapPartitions(iter => {

  val records = new util.LinkedList[String]()

  // 将每个 partition 中的多行数据,以 100 为长度作为一组,进行一次批处理
  iter.grouped(100)
    .foreach(rows => {
      rows.foreach(row => records.add(JSON.toJSONString(row, false)))
    })

  // 重新将一组处理后的数据,封装成 Iterator 类
  new AbstractIterator[String]() {

    var shouldOutput = true

    override def hasNext: Boolean = shouldOutput

    override def next(): String = {
      shouldOutput = false
      JSON.toJSONString(records, false)
    }
  }
})

// 反序列成 list 对象,再将 list 对象 flat 成单个元素,同时,过滤掉其中空白行
val filteredEmptyLine = dataset
  .filter(_ != null)
  .filter(!_.equals(""))
  .map(JSON.parseObject(_, classOf[util.LinkedList[String]]))
  .filter(_ != null)
  .filter(_.size() != 0)
  .flatMap(_.toArray)
  .filter(_ != null)
  .map(JSON.toJSONString(_, false))
  .filter(!_.equals(""))

Case class

和 class 的 8 个不同之处

// 定义
scala> case class Person(name:String, age:Int)
defined class Person

// 初始化
scala> val bj = Person("Benedict Jin", 18)
bj: Person = Person(Benedict Jin,18)

// 参数的访问权限都是 public
scala> bj.name
res0: String = Benedict Jin

scala> bj.age
res1: Integer = 18

// 更加简洁的 toString 打印
scala> bj.toString
res2: String = Person(Benedict Jin,18)

// 默认实现了 hashCode 和 equals 方法
scala> bj.hashCode
res3: Int = 1059149039

scala> Person("Benedict Jin", 18).hashCode
res4: Int = 1059149039

scala> Person("Benedict Jin", 18) == bj
res5: Boolean = true

// 默认实现了 java.io.Serializable 接口,支持序列化
scala> import java.io._
import java.io._

scala> val bos = new ByteArrayOutputStream
bos: java.io.ByteArrayOutputStream =

scala> val oos = new ObjectOutputStream(bos)
oos: java.io.ObjectOutputStream = java.io.ObjectOutputStream@62ddd21b

scala> oos.writeObject(bj)

scala> val obj = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray)
).readObject()
obj: Object = Person(Benedict Jin,18)

scala> Person("Benedict Jin", 18) == obj
res6: Boolean = true

// 支持 match
scala> bj match {
     |   case Person("Benedict Jin", 18) => println("matched")
     |   case _ => println("non-matched")
     | }
matched

// 默认继承 scala.Product 类,并实现了其中的方法
scala> bj.productArity
res7: Int = 2

scala> bj.productIterator.next()
res8: Any = Benedict Jin

scala> bj.productElement(1)
res9: Any = 18

scala> bj.productPrefix
res10: String = Person

打印 trait 中的字段

scala> trait T{
     |   var t: String = ""
     | }
defined trait T

// 正常情况下,不会打印 trait 中的字段
scala> case class Person(name:String)extends Serializablewith T
defined class Person

scala> Person("Benedict Jin")
res6: Person = Person(Benedict Jin)

// 通过 ScalaRunTime._toString(this) 来覆写 toString 方法,可以避免 case class 的 toString 方法被篡改
scala> import scala.runtime.ScalaRunTime
import scala.runtime.ScalaRunTime

scala> case class Person2(name:String)extends Serializablewith T{
     |   override def toString: String = ScalaRunTime._toString(this)
     | }
defined class Person2

scala> Person2("Benedict Jin").toString
res7: String = Person2(Benedict Jin)

// 重写 toString 来打印 trait 中的字段
scala> case class Person3(name:String)extends Serializablewith T{
     |   override def toString: String = s"Person3(${this.name},${this.t})"
     | }
defined class Person3

scala> val p3 = Person3("Benedict Jin")
p3: Person3 = Person3(Benedict Jin, )

scala> p3.t = "t"
p3.t: String = t

scala> p3.toString
res8: String = Person3(Benedict Jin, t)

单元测试

详见,《在不同场景下, 如何选择合适的 JVM 语言

常见问题

classOf 和 getClass 的区别

scala> class A
  defined class A

scala> val a = new A
  a: A = A@1d483de4

// A 的任意子类
scala> a.getClass
  res0: Class[_ <: A] = class A

// A 类本身
scala> classOf[A]
  res1: Class[A] = class A

// 两者是等价的
scala> a.getClass == classOf[A]
  res2: Boolean = true

// getClass 的返回值,是不能直接赋值给 Class[A] 的
scala> val c: Class[A] = a.getClass
  <console>:13: error: type mismatch;
   found   : Class[?0] where type ?0<: A
   required: Class[A]
  Note: ?0 <: A, but Java-defined class ClassisinvariantintypeT.
  You may wish to investigate a wildcard type suchas`_<: A`. (SLS 3.2.10)
         val c:Class[A] = a.getClass
                            ^

// 需要声明 Class[_ <: A] 才行,类似于  Java  里面的 Class<? extends A>
scala> val c: Class[_ <: A] = a.getClass
  c: Class[_ <: A] = class A

// 但是获取全限定名的返回值,却是一样的
scala> c.getName
  res3: String = A

scala> classOf[A].getName
  res4: String = A

稳定的标识符模式

// 如果不用 ` 反引号将 a / b / c 变量括起来,那么这些变量,其实就变成了指向 i 的别名,已经和 match 外层的 a / b / c 变量无关了
scala> def m(i: Int) = {
     |   val a = 3
     |   val b = 2
     |   val c = 1
     |   i match {
     |     case `a` => 0
     |     case `b` => -1
     |     case `c` => 4
     |     case _ => 2
     |   }
     | }
m: (i: Int)Int

scala> m(1)
res17: Int = 4

scala> m(2)
res18: Int = -1

scala> m(3)
res19: Int = 0

// 另外,使用反引号将变量括起来之后,scala 会在字节码层面做优化
/*
 0: iconst_3
 1: istore_2
 2: iconst_2
 3: istore_3
 4: iconst_1
 5: istore          4
 7: iload_1
 8: istore          5
10: iload_2
11: iload           5
13: if_icmpne       22
16: iconst_0
17: istore          6
19: goto            50
22: iload_3
23: iload           5
25: if_icmpne       34
28: iconst_m1
29: istore          6
31: goto            50
34: iload           4
36: iload           5
38: if_icmpne       47
41: iconst_4
42: istore          6
44: goto            50
47: iconst_2
48: istore          6
50: iload           6
52: ireturn
 */

Tips: Full code is here and here .

参考

在函数调用里面,对变量进行赋值

scala> val map = new java.util.LinkedHashMap[String, String]()
map: java.util.LinkedHashMap[String,String] = {}

scala> map.put("1", "1")
res0: String = null

scala> map.put("2", "2")
res1: String = null

scala> map.put("3", "3")
res2: String = null

scala> map
res3: java.util.LinkedHashMap[String,String] = {1=1, 2=2, 3=3}

scala> var s = "1 "
s: String = "1 "

scala> map.containsValue(s)
res4: Boolean = false

// 虽然已经将 s.trim 赋值给 s 变量了,但是传入 map.containsValue 方法的仍然是未进行 trim 操作之前的 s 变量
// 这里和 Java 是不一样的,Java 会将已经赋值之后的变量值,传入到方法中
scala> map.containsValue(s = s.trim)
res5: Boolean = false

scala> s
res6: String = 1

scala> s.equals("1")
res7: Boolean = true

Java 的 Lambda 表达式转为 Scala 的 Function

Java 的 Lambda 表达式

final static ThreadLocal<String> BLOG_ID = ThreadLocal.withInitial(() -> "null");

反面示例

// 直接填入 lambda 表达式,IDE 是不会报错的,但是会在代码编译阶段报错
private val BLOG_ID: ThreadLocal[String] = ThreadLocal.withInitial(() -> "null")

// 即便把 function 单独拿出来作为 Supplier 变量进行申明,仍然会出现编译错误
val blogIdFunc: java.util.function.Supplier[String] = () => "null"

// 具体编译报错,如下:
val func: java.util.function.Supplier[String] = () => "null"
 error: type mismatch;
 found   : () => String
 required: java.util.function.Supplier[String]
       val func: java.util.function.Supplier[String] = () => "null"
                                                          ^

正面示例

// 写成 new Supplier[T]{...} 的写法之后,可以完成编译
// 但是,编译器仍然会提示,可以将代码优化为 () => "null"
val blogIdFunc: java.util.function.Supplier[String] = new Supplier[String] { override def get(): String = "null" }
private val BLOG_ID: ThreadLocal[String] = ThreadLocal.withInitial(blogIdFunc)

补充

// 如果该部分代码仍然需要运行在 JDK7 及以下版本的 JVM 环境中,则可以改成普通的 "初始化 ThreadLocal" 方式
private val BLOG_ID: ThreadLocal[String] = new ThreadLocal[String]() {
  override def initialValue(): String = "null"
}

如何传递变长参数

描述

scala> def detail(d: Any*): Unit = println("%s_%s".format(d))
detail: (d: Any*)Unit

scala> detail("a", "b")
java.util.MissingFormatArgumentException: Format specifier '%s'
  at java.util.Formatter.format(Formatter.java:2519)
  at java.util.Formatter.format(Formatter.java:2455)
  at java.lang.String.format(String.java:2940)
  at scala.collection.immutable.StringLike$class.format(StringLike.scala:318)
  at scala.collection.immutable.StringOps.format(StringOps.scala:29)
  at .detail(<console>:11)
  ... 32 elided

解决

// 在函数中,想要将传入的变长参数,保持成多个参数的特性,传递下去的话,需要声明 : _*
scala> def detail(d: Any*): Unit = println("%s_%s".format(d: _*))
detail: (d: Any*)Unit

scala> detail("a", "b")
a_b

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Creative Selection

Creative Selection

Ken Kocienda / St. Martin's Press / 2018-9-4 / USD 28.99

Hundreds of millions of people use Apple products every day; several thousand work on Apple's campus in Cupertino, California; but only a handful sit at the drawing board. Creative Selection recounts ......一起来看看 《Creative Selection》 这本书的介绍吧!

SHA 加密
SHA 加密

SHA 加密工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具