长沙教育网站开发,动画设计师资格证书,安阳区号12345,17网站一起做网店怎么拿货目录 Scala 01 —— Scala基础一、搭建Scala开发环境安装Scala编译器在IDEA中进行scala编码 二、Scala简介与概述Scala简介Scala概述Scala代码规范 三、理解Scala变量与数据类型Scala的变量与常量Scala和Java变量的区别 Scala的数据类型 四、Scala的程序逻辑1.表达式2.运算符3.… 目录 Scala 01 —— Scala基础一、搭建Scala开发环境安装Scala编译器在IDEA中进行scala编码 二、Scala简介与概述Scala简介Scala概述Scala代码规范 三、理解Scala变量与数据类型Scala的变量与常量Scala和Java变量的区别 Scala的数据类型 四、Scala的程序逻辑1.表达式2.运算符3.顺序控制4.分支控制5. 循环6.迭代器 五、集合导包泛型链表集合映射数组字符串插值下划线表示任意字符模式匹配 六、方法与函数七、数组方法1.创建定长|变长数组创建定长数组创建变长数组 2.基本操作获取长度、检查空状态全部元素检查、存在性、包含行检查起始/结束匹配、逆向定位元素数据迁移添加元素删除元素修改和提取其他算法其他算法 Scala 01 —— Scala基础
Scala是Spark的基础我们需要研究将大数据引入到Spark的特定架构。
Scala集合很重要
作为数据结构解决存储问题包含了大量算子、解决问题的库
由于Scala集合很多我们在学习的时候需要先学一个作为共性
然后在掌握差异化的部分(其实高级程序语言不是很依赖差异化的数据结构)
一、搭建Scala开发环境
安装Scala编译器 安装scala-2.12.10.msi(资源位于E:\BigData\software\snd_download) 检查scala安装情况 在dos窗口输入scala检查是否能够进入编译器。进行简单的scala命令计算
在IDEA中进行scala编码
File - Settings - Plugins - MarketPlace中搜索scala插件 安装后记得重启 新建一个空的Maven工程删除main包和test包新建一个包名为Scala将该包mark directory as source root设置成源码包在该包下新建一个 Scala Class中的Object类 在 Project Structure中在Libraries 和 Global Libraries中添加 Scala SDK(每新建一个工程都要重新配置一次) 检查基本配置
pom.xml中的maven.compiler.source和maven.compiler.target都是8Project Structure中的Modules的Language Level为8-Lambdas,type annotations etc.Settings中的Build,Execution,Deployment中的Compiler的Java Compiler的Project bytecode version为8Target bytecode version为1.8
进行代码测试
package cha01object Test01 {def main(args: Array[String]): Unit {println(hello scala)}
}二、Scala简介与概述
Scala简介
Scala源自Java Scala构建在JVM之上Scala与Java兼容、互通(❗ 不要混合编码) Scala的优势 多范式编程支持面向对象编程、面向过程编程、函数式编程等多种编程范式(函数式编程是Spark的重中之重)表达能力强代码精简 大数据与Scala Spark采用Scala语言设计 提供的API更加优雅基于JVM的语言更融入Hadoop生态圈
Scala概述 面向对象特性 每个值都是对象 在Scala中包括基本类型都是对象。每个值都是某个类的实例每个操作都是方法调用。 对象的数据类型和行为由类(Class)和特征(Trait,类似于interface)描述 Trait不仅具有Java中接口的功能还引入了混入式多重继承的概念同时可以包含方法的实现和变量定义。 混入式多重继承允许在实例化时或之后将多个Trait混入一个类中 动态混入直接面向对象编写代码。允许在对象实例化时或对象使用过程中动态地将一个或多个Trait添加到类的实例中(方便开发者根据需要在运行时为对象添加新的行为或状态) 函数式编程 每个函数都是一个值 在Scala中每个函数都必须返回一个值。在Scala中函数不需要写return最后一个表达式的类型就是函数的返回类型。 支持高阶函数柯里化(currying)样例类(case class)及模式匹配… 高阶函数Scala中提供了大量的函数90%的算法都无需我们自己去写我们只需要拼图即可。 柯里化多参数列表 样例类Java类的简化版 模式匹配将数据延伸、变形、转化的过程语法简化 Scala是静态类型语言 扩展性隐式类、字符串插值 隐式Scala包括隐式类、隐式变量、隐式函数(Scala中的高阶函数中包含了大量的隐式函数) 可用于将多个函数的共性逻辑声明为一个隐式函数并将其自动传进去可实现隐式转换
Scala代码规范
通常一行一条命令末尾无需添加分号。若一行包含多条命令两两之间用分号隔开。(【建议】非特殊语法不允许主动用分号隔开命令)可以实现过程中导包过程中定义函数
三、理解Scala变量与数据类型
Scala的变量与常量
创建变量 创建变量必须赋初值可以不指定变量的具体类型如var a 12。则变量的类型由首次赋值的值类型确定且锁定。(通常不指定)若声明时已指定变量类型如var a:String “”则类型不可更改。
var variableName[:Type] V创建常量 创建常量且初始化后常量的类型和值都将锁定。
val valName[:Type] V类型别名
type Alias Type
type S String
var str:S henryScala和Java变量的区别
是否指定类型Java在声明变量的时候必须指定变量的类型Scala在声明变量的时候可以不指定类型是否必须赋初值Java在声明变量的时候可以不赋初值Scala在声明变量的时候必须赋初值
Scala的数据类型
在Scala中所有的数据类型都是引用类型
Any Anything(引用类型下的基本类型并非单独的模块) Unit (≈void)BooleanByteShortIntLongFloatDoubleBigIntBigDecimal AnyRef(引用类型) StringListSelfDefinedClass Null是所有引用类型的子类Nothing是所有类的子类 Tuple 元组 定义 可以存放不同类型的元素 一旦创建就不能修改其中的元素 元组长度范围从1~22 表示方法 使用小括号()来表示一个元组。特别注意()在Scala中有多重含义可能表示 数组下标提取如array(int)。方法的参数列表如method(p1, p2, ...)。 声明和初始化方法 // 简单声明和初始化
val tp2 (java, 88) // syntatic sugar指那些可读性强的语法// 明确指定元组类型
val tp2: (String, Int) (java, 88)// 使用Tuple类显式声明
val tp2: Tuple2[String, Int] Tuple2(java, 88)元组操作 在Scala中通过._n来访问元组中的元素。元组的索引是从1开始的。tp.productIterator()的返回值Iterator[Any]是为了方便遍历元组中的所有元素 // 获取元组中的元素
println(s${tp2._1} ${tp2._2})// 对元组进行迭代
val it:Iterator[Any] tp2.productIterator
tp2.productIterator.foreach(println) // 遍历输出元组中的所有元素四、Scala的程序逻辑
1.表达式 一切皆是表达式(val expr {})所有的构造都有值并且可以产生结果不需要写return 条件判断 val result if(ab) greater else lesser循环 val squares for(i - 1 to 5) yield i*i代码块 val result {val temp a * btemp 10
}赋值:Scala的赋值表达式不是为了返回一个值 惰性表达式使用时执行(lazy val expr {}) 2.运算符
赋值 - * / %
算术 - * / %(没有和–)
关系 !
逻辑 || !
在Scala对象上使用运算符时实际上是在调用一个方法。因此Scala支持运算符的重载例如下面的例子对进行重载实现了复数的加法
case class Complex(re: Double, im: Double) {def (that: Complex): Complex Complex(this.re that.re, this.im that.im)
}val a Complex(1.0, 2.0)
val b Complex(3.0, 4.0)
val c a b // 调用a的方法传入b作为参数
3.顺序控制
注意懒加载的情况
4.分支控制
var result if (fig) 1 else 0 // 类似三元运算符5. 循环
// by 可以指定步长
val range1 1 to 10 by 2 // to 前闭后闭
val range2 1 until 10 by 3 // until 前闭后开for(i - 1 to 3){print(i )
}for(i - 1 until 3){print(i )
}// 循环守卫 该写法不可以写成常量的形式
for(i - 1 to 3 if i ! 2){print(i )
}// 引入变量
for(i - 1 to 3; j4-i){print(j )
}// 正向遍历
for(i - 0 until arr.length){val item arr(i)
}// 反向遍历
for(i-(0 until arr.length).reverse){val item arr(i)
}在Scala中为了更好地适应函数化编程特定去掉了break和continue。
// 如何实现continue的效果- 循环守卫
for(i - 1 to 10 if (i ! 2 i ! 3)){print(i )
}yield关键字将原始的数组进行转换会产生一个新的数组原始的数组不变
val arr Array(1,2,3,4,5)
//arr:Array[Int] Array(1,2,3,4,5)
val res for(e - arr) yield e*2
//res:Array[Int] Array(2,4,6,8,10)
val res1 arr.map(_*2)
//res1:Array[Int] Array(2,4,6,8,10)val arr Array(1,2,3,4,5)
val r arr.filter(_ % 2 0).map(_ * 10)
println(r.toBuffer)filter是过滤接收一个返回值为boolean的函数map相当于是将数组中的每一个元素取出来应用传进去的函数
6.迭代器
迭代器的三种遍历方法(123为优先级排序)
val it:Iterator[Any] tp2.productIteratorit.foreach(println) // 使用了Scala的已有函数println(返回类型恰好是Unit) 1
it.foreach(eprintln(e)) // 调用自定义函数(一次性) 2
def show(e:List[Int])println(e) // 调用自定义函数(可重用) 3
it.foreach(show)五、集合 一般集合的创建都写成常量形式 在大数据中默认使用的是不可变类型 .var Variable 更改为变量Specify type显示返回类型(适合初学者用)。为了方便设置为自动勾选点击Settings在Type Annotations选项卡里勾选Local Definition选项。
导包
import scala.collection.immutable.Set // 导入具体类
import scala.collection.mutable._ // 导入包内所有类
import scala.collection.mutable.{ListBuffer,ArrayBuffer} // 导入包内部分类默认导入不可变集合包 使用可变集合前需要先import scala.collection.mutable再通过mutable.T使用 import scala.collection.mutable.Set
val set mutable.Set()scala.collection.mutable和scala.collection.immutable的目录结构 scala.collection.mutable scala.collection.immutable 泛型
泛型用中括号表示[T1,…,TN]
链表
import scala.collection.immutable.List
import scala.collection.mutable.ListBuffer //可以代替链表高效插删分组 list.grouped(N)表示每N个元素为一组若非N的整数倍则最后一组含少于N个元素
val list List(1, 2, 3, 4, 5, 6)
list.grouped(3).foreach(println)
//(1,2,3)
//(4,5,6)val it: Iterator[List[Int]] list.sliding(3,1)// 滑动窗口 list.sliding(3,3) list.grouped(3) // list.sliding(窗口长度窗口每次移动距离)集合
import scala.collection.immutable.Set
import scala.collection.mutable.Set差 差集 set1和set2的顺序会影响正负 当两个不同类型的集合进行交并差的时候 是左边的集合类型决定了结果类型
val diff: mutable.Set[Int] set2.diff(set1)
val diff2: mutable.Set[Int] set2 ~ set1交
val union: mutable.Set[Int] set1.union(set2) // 交集
val union2: mutable.Set[Int] set1 | set2 // 交集并
val intersect: mutable.Set[Int] set1.intersect(set2) // 并集
val intersect2: mutable.Set[Int] set1 set2映射
import scala.collection.immutable.Map
import scala.collection.mutable.Mapval map mutable.Map.empty;映射之间
var map Map[String,String](name - jason,age - 50,test_100 - test_100,test_101 - test_101)
val map2 Map[String,String](brand-apple,sex-男)
map (city - 北京)
map (city - 南京)// 更新键的值var combineMap: Map[String, String] map map2// 合并两个map
val newMap: Map[String, String] combineMap -- map.keys// 从一个map重删去另一个map的键
combineMap - (city,name) // 删除指定的key
println(combineMap.get(age).get) // 获取指定key的值
println(combineMap.get(age).getOrElse(不存在))映射与元组
val map:mutable.Map[String,Int] mutable.Map.empty
map ((java,88)) // 外层括号表示方法的参数列表内层括号表示是一个二元组
map Array((scala,76),(hadoop,79)) // 一次性放入数组
import scala.Array
import scala.collection.mutable.ArrayBufferval array mutable.ArrayBuffer((5,100),(3,88),(2,60),(4,74),(2,52),(5,80),(3,83),(1,59),(1,77),(4,45)
)
// 尾部追加
array.append((2,36),(4,77))
array((2,36))
// 向一个集合中添加另一个集合
array.appendAll(Array((1,100),(2,200)))
arrayArray((1,100),(2,200))
// 前置添加
array.prepend((2,36),(4,77))
array.prependAll(Array((1,100),(2,200)))数组与元组 :面向集合
val tp2s: ArrayBuffer[(Int, Int)] array : Tuple2(1, 100)
val tp3s: ArrayBuffer[(Int,Int)] Tuple2(2,100) : array字符串插值
s插值器
支持转义符
val age 18
if(age18)println(s${age}岁是成年)
elseprintln(s${age}岁是未成年)f插值器
支持转义符和格式占位符
val height 1.9d
val name James
val introduction f$name%s is $height%2.2f meters tallraw插值器
不支持转义符
val rawString rawNewline character: \n
println(rawString) // 输出Newline character: \n而不是换行下划线表示任意字符 通配符导入 在导入包时下划线用作通配符表示导入该包下的所有成员。 import scala.collection.mutable._省略参数 val nums List(1,2,3)
val doubled nums.map(_ * 2)占位符语法 def add(a:Int,b:Int):Int ab
val addTwo add(2,_:Int) // 创建一个新的函数其中第一个参数固定为2忽略参数 将不关心的参数忽略起到一定程度上的减少冗余 val (_,value) (1,hello)模式匹配
case _ 表示没有被之前的 case 分支匹配的值类似于 Java 中 switch-case 语句中的 default 关键字。case List(id,java,score:Int)用于匹配列表结构
// 字符串与条件守卫
val rst henryqq.com match {case a if a.matches(\\w\\w{2,}\\.(com|cn)) emailcase a if a.matches(1[3-9]\\d{9}) handsetcase NULL nothingcase _ unknown
}
// 数值
val rst 1 match {case 1 chargecase 2 find balancecase 3 promotioncase _ not supported
}
// 元组
val rst (7,101) match {case (3,_) (3,1)case (5,a) if(a%20) (5,0) else (5,1)case _ (0,0)
}
// 列表
val rst List(1,hive,88) match {case List(id,java,score:Int) if(score60) (java,pass) else (java,fail)case _ (SKIP,NONE)
}
// 类型
val rst value match {case a:Int (int,a)case a:(Int,Int) (Tuple2,a)case a:String (String,a)case _ (Unknown,1)
}
// 嵌套
val rst (1,(java,67)) match {case (1,(sub:String,score:Int)) (sub,score)case _ (UnknownFormat,1)
}六、方法与函数 从Java-Scala为什么我们需要划分方法与函数的界限 在Java中方法这个术语被用来描述类或对象的行为而Java没有像Scala那样显式地使用函数这个概念。这是因为Java主要是一种面向对象的编程语言而不直接支持函数式编程的特性。 Scala是多范式编程语言支持面向对象和函数式编程两种范式。在Scala中函数是一等公民意味着它们可以像任何其它值一样被传递和操作。这种设计决定了方法(定义在类或对象中的行为)和函数(可以独立于类或对象存在的代码块)之间有明确的区别。 方法与函数的异同· 同都可以执行代码块并返回结果。 异方法不能直接作为值传递而函数可以直接作为值传递。 方法调用依赖于对象实例或类而函数可以独立于任何类或对象存在。 将函数作为参数传递的三种方式
def show(e:List[Int])println(e)
it.foreach(show) // 【自定义】函数(重用) 3
it.foreach(eprintln(e)) // 【自定义】函数(一次性) 2
it.foreach(println) // 调用scala【已定义】函数 1创建函数 val func:(参数类型) (返回类型) (参数列表) {函数体}
// 接受一个整数列表作为参数并返回列表中所有元素之和的函数
val sum:(List[Int])Int(numbers){numbers.sum
}创建方法 def func(参数列表):返回类型{方法体}
// 求和方法
def add(a:Int,b:Int):Int{ab
}可变参数一个参数列表只能有一个可变参数且必须在参数列表的末尾 def func(...,可变参数可变参数类型*):返回类型{方法体}
def func(numbers:Int*):Int{numbers.sum
}函数作为参数主流 def func(...,参数方法名:(参数列表)返回类型,...):返回类型{方法体}
def applyOperation(x:Int,y:Int,operation:(Int,Int)Int):Int operation(x,y)val sum applyOperation(5,3,(a,b)ab)
println(sum)函数柯里化(Currying)
def func(init:Int,a:Int*) {...} def func(init:Int)(a:Int*) {...}
def func1(init: Int)(a: Int*): Int init a.sumdef func2(init: Int, a: Int*): Int init a.sumval result1 func1(10)(1, 2, 3) // 需要两步调用func1返回一个函数
val result2 func2(10, 1, 2, 3) // 一步调用直接传递所有参数println(result1) // 输出16
println(result2) // 输出16func1是一个柯里化函数是将接收多个参数的函数转换成接受单一参数的函数的过程这些单一参数的函数返回接收下一个参数的新函数。
柯里化的作用1.可以固定一部分参数便于进行函数的服用
2.有利于进行延迟计算 隐式参数(implicit) implicit val f (a:Int,b:Int) a/b def divide(init:Int)(a:Int*)(implicit f:(Int,Int)Int):Int f(init,a)
implicit val divider: (Int, Int) Int (a, b) a / bdef divide(init: Int)(a: Int*)(implicit f: (Int, Int) Int): Int f(init, a.sum)val result divide(10)(1, 2, 3) // 使用隐式参数divider
println(result) // 输出2隐式参数允许你省略调用函数时传递常用或可以由上下文推断的参数简化代码。
隐式参数中需要将函数作为方法参数
七、数组方法
1.创建定长|变长数组
创建定长数组
// new 关键字让所有元素初始化为0
val arr1 new Array[Int](8)
val arr2 Array[Int](10)
val arr3 Array(hadoop,storm,spark)创建变长数组
import scala.collection.mutable.ArrayBufferval empty: ArrayBuffer[Int] ArrayBuffer.empty
val ab1: ArrayBuffer[Int] ArrayBuffer(1, 2, 3)2.基本操作
获取长度、检查空状态
val length buffer.length
val bool buffer.isEmpty
val bool buffer.nonEmpty全部元素检查、存在性、包含行检查
val bool buffer.forall(f: T Boolean) // 检查是否所有元素都满足条件
println(array.forall(_ 10)) // 判断数组是否所有元素都10val bool buffer.exists(f: T Boolean) // 检查是否存在满足条件的元素
println(array.exists(_ 10)) // 判断数组是否存在10的元素val bool buffer.contains(t: T)
val bool buffer.containsSlice(Array(10,9,20)) // 是否包含完整的子序列(数量顺序内容)array.corresponds(Array(16, 20, 14, 11, 6, 9, 8, 20))(__) // 判定该数组的每个元素是否与传入的数组序列的每个元素都符合传入函数的规则
array.sameElements(Array(17,21,15,12,7,10,9,20)) // 判定该数组的每个元素是否与传入的数组序列的每个元素相同起始/结束匹配、逆向
val bool buffer.startsWith(Array(17,21,15))// 判定数组是否是以该元素片段开头
val bool buffer.endsWith(Array(10,9,20))// 判定数组是否是以该元素片段结尾
val reverse: ArrayBuffer[T] buffer.reverse定位元素
val index buffer.indexOf(15)// 查找该元素在数组中对应的下标
val index buffer.indexOfSlice(Array(7, 10, 9))// 查找该片段在数组中的第一个元素的下标
var index array.indexWhere(_ 10)// 查找第一个符合条件的元素下标val lastIndex buffer.lastIndexOf(15)// 从尾部开始查找查找该元素在数组中对应的下标
val lastIndex buffer.lastIndexOfSlice(Array(7, 10, 9))// 从尾部开始查找查找该片段在数组中的第一个元素的下标
var lastIndex array.lastIndexWhere(_ 10)// 从尾部开始查找查找第一个符合条件的元素下标数据迁移
val ints: Array[Int] array.clone()// 复制数组的元素和结构
array.copyToArray(arr,0,2)// 将array数组中下标范围为[0,2)的元素拷贝到arr数组中
array.copyToBuffer添加元素
// buffer.append(e:T*)
buffer.append((1,111))
// buffer.prepend(e:T*) :
buffer.prepend((2,112))
// buffer.appendAll(es:TraversableOnce)
buffer.appendAll(Array((3,113)))
// buffer.prependAll(es:TraversableOnce) :
buffer.prependAll(Array((4,114)))
buffer.insert(0,(3,112))
buffer.insertAll(0,Array((1,451)))
// 最终类型由左侧表达式的类型决定
val combine:ArrayBuffer[T] buffer seq:GenTraversableOnce[T]
val newBuffer: ArrayBuffer[T] buffer : ((4,5)) // 用于在数组缓冲的末尾添加元素buffer:t即将元素添加到buffer的末尾
val newBuffer: ArrayBuffer[T] ((4,5)) : buffer// 用于在数组缓冲的开头添加元素buffer:t即将元素添加到buffer的末尾如何理解:和: 可以将:看作是数组缓冲的象征而指向要添加的元素的位置buffer : t可以被视为将元素t添加到buffer的末尾
删除元素
val left: ArrayBuffer[T] buffer - (T)
val left: ArrayBuffer[T] buffer -- (seq:TraversableOnce[T])
buffer - (T)
buffer -- (seq:TraversableOnce[T])
T t buffer.remove(index:Int)// 删除指定下标的元素并返回被删除的元素
buffer.remove(index:Int, count:Int)// 从index位置开始删除count个元素
buffer.clear()
val left1: ArrayBuffer[T] buffer.drop(size:Int) // 左删除
val left2: ArrayBuffer[T] buffer.dropRight(size:Int) // 右删除
// 元素已升序排序时推荐 : 从左1开始删除连续满足条件的元素若左1就不满足则不删除
val left3: ArrayBuffer[T] buffer.dropWhile(f:TBoolean) // 左删
buffer.trimEnd(size:Int) // 右删除size个元素
buffer.trimStart(size:Int) // 左删除size个元素
buffer.reduceToSize(size:Int) // 削减容量(左保留)
val distinct: ArrayBuffer[(Int, Int)] buffer.distinct // 去重修改和提取 array.patch(from,seq,replaced) replaced0插入 replaced0seq.nonEmpty()替换 replaced0seq.isEmpty()删除 Option类型用于表示一个值可能存在也可能不存在的情况。Option 有两种形态Some 和 None。Some 包含一个值而 None 表示没有值。使用 Option 可以避免空指针异常。
array.update(2,30)// 将下标为2的元素值改为30
val upd: Array[Int] array.updated(2, 30) // 返回一个新数组val upd array.patch(2, Array(100, 200), 2) // 将从下标2开始的两个元素分别替换为100和200
val upd2 array.patch(2, Array[Int](), 2) // 相当于删除val item buffer.apply(index:Int)// 用于获取 ArrayBuffer 中给定索引位置的元素
val item buffer.applyOrElse(index:Int,f:Int-T)// 尝试获取给定索引的元素如果索引超出范围或不存在则调用函数 f
val opt: Option[(Int, Int)] buffer.find(f:TBoolean)// 搜索第一个满足给定条件的元素并返回一个 Option 类型的结果。如果找到符合条件的元素则返回 Some(元素)如果没有找到则返回 None。其他算法
val arr Array(2,1,3,4,5)
println(arr.sumarr.sum)// 求和
println(arr.maxarr.max)// 求最大值
println(arr.sortedarr.sorted.toBuffer)// 排序array.patch(from,seq,replaced)
replaced0插入 replaced0seq.nonEmpty()替换 replaced0seq.isEmpty()删除
Option类型用于表示一个值可能存在也可能不存在的情况。Option 有两种形态Some 和 None。Some 包含一个值而 None 表示没有值。使用 Option 可以避免空指针异常。
array.update(2,30)// 将下标为2的元素值改为30
val upd: Array[Int] array.updated(2, 30) // 返回一个新数组val upd array.patch(2, Array(100, 200), 2) // 将从下标2开始的两个元素分别替换为100和200
val upd2 array.patch(2, Array[Int](), 2) // 相当于删除val item buffer.apply(index:Int)// 用于获取 ArrayBuffer 中给定索引位置的元素
val item buffer.applyOrElse(index:Int,f:Int-T)// 尝试获取给定索引的元素如果索引超出范围或不存在则调用函数 f
val opt: Option[(Int, Int)] buffer.find(f:TBoolean)// 搜索第一个满足给定条件的元素并返回一个 Option 类型的结果。如果找到符合条件的元素则返回 Some(元素)如果没有找到则返回 None。其他算法
val arr Array(2,1,3,4,5)
println(arr.sumarr.sum)// 求和
println(arr.maxarr.max)// 求最大值
println(arr.sortedarr.sorted.toBuffer)// 排序 文章转载自: http://www.morning.hwnqg.cn.gov.cn.hwnqg.cn http://www.morning.sqfrg.cn.gov.cn.sqfrg.cn http://www.morning.nnmnz.cn.gov.cn.nnmnz.cn http://www.morning.bzkgn.cn.gov.cn.bzkgn.cn http://www.morning.xjkfb.cn.gov.cn.xjkfb.cn http://www.morning.bpmnj.cn.gov.cn.bpmnj.cn http://www.morning.nbwyk.cn.gov.cn.nbwyk.cn http://www.morning.lfbsd.cn.gov.cn.lfbsd.cn http://www.morning.lpzyq.cn.gov.cn.lpzyq.cn http://www.morning.yubkwd.cn.gov.cn.yubkwd.cn http://www.morning.rdlrm.cn.gov.cn.rdlrm.cn http://www.morning.mdnnz.cn.gov.cn.mdnnz.cn http://www.morning.tfsyk.cn.gov.cn.tfsyk.cn http://www.morning.fpczq.cn.gov.cn.fpczq.cn http://www.morning.cpfbg.cn.gov.cn.cpfbg.cn http://www.morning.zqcdl.cn.gov.cn.zqcdl.cn http://www.morning.ffydh.cn.gov.cn.ffydh.cn http://www.morning.rxlck.cn.gov.cn.rxlck.cn http://www.morning.rnygs.cn.gov.cn.rnygs.cn http://www.morning.yqsr.cn.gov.cn.yqsr.cn http://www.morning.dkmzr.cn.gov.cn.dkmzr.cn http://www.morning.zyrp.cn.gov.cn.zyrp.cn http://www.morning.hotlads.com.gov.cn.hotlads.com http://www.morning.lprfk.cn.gov.cn.lprfk.cn http://www.morning.kbkcl.cn.gov.cn.kbkcl.cn http://www.morning.wxccm.cn.gov.cn.wxccm.cn http://www.morning.ygrkg.cn.gov.cn.ygrkg.cn http://www.morning.snnkt.cn.gov.cn.snnkt.cn http://www.morning.rqxmz.cn.gov.cn.rqxmz.cn http://www.morning.zmtrk.cn.gov.cn.zmtrk.cn http://www.morning.xbhpm.cn.gov.cn.xbhpm.cn http://www.morning.tbplf.cn.gov.cn.tbplf.cn http://www.morning.dfbeer.com.gov.cn.dfbeer.com http://www.morning.mnygn.cn.gov.cn.mnygn.cn http://www.morning.bfnbn.cn.gov.cn.bfnbn.cn http://www.morning.lptjt.cn.gov.cn.lptjt.cn http://www.morning.dmldp.cn.gov.cn.dmldp.cn http://www.morning.lbfgq.cn.gov.cn.lbfgq.cn http://www.morning.bpmfg.cn.gov.cn.bpmfg.cn http://www.morning.pwdgy.cn.gov.cn.pwdgy.cn http://www.morning.csgwd.cn.gov.cn.csgwd.cn http://www.morning.wjlkz.cn.gov.cn.wjlkz.cn http://www.morning.zfxrx.cn.gov.cn.zfxrx.cn http://www.morning.kongpie.com.gov.cn.kongpie.com http://www.morning.ybqlb.cn.gov.cn.ybqlb.cn http://www.morning.brnwc.cn.gov.cn.brnwc.cn http://www.morning.byrlg.cn.gov.cn.byrlg.cn http://www.morning.xnltz.cn.gov.cn.xnltz.cn http://www.morning.clpfd.cn.gov.cn.clpfd.cn http://www.morning.c7510.cn.gov.cn.c7510.cn http://www.morning.bsxws.cn.gov.cn.bsxws.cn http://www.morning.xlmgq.cn.gov.cn.xlmgq.cn http://www.morning.lynkz.cn.gov.cn.lynkz.cn http://www.morning.ghfrb.cn.gov.cn.ghfrb.cn http://www.morning.fplqh.cn.gov.cn.fplqh.cn http://www.morning.rttkl.cn.gov.cn.rttkl.cn http://www.morning.qhrdx.cn.gov.cn.qhrdx.cn http://www.morning.rgrys.cn.gov.cn.rgrys.cn http://www.morning.dwkfx.cn.gov.cn.dwkfx.cn http://www.morning.knqzd.cn.gov.cn.knqzd.cn http://www.morning.kjksn.cn.gov.cn.kjksn.cn http://www.morning.jwsrp.cn.gov.cn.jwsrp.cn http://www.morning.rrcrs.cn.gov.cn.rrcrs.cn http://www.morning.hmsong.com.gov.cn.hmsong.com http://www.morning.tqsnd.cn.gov.cn.tqsnd.cn http://www.morning.rsjf.cn.gov.cn.rsjf.cn http://www.morning.lnwdh.cn.gov.cn.lnwdh.cn http://www.morning.msfqt.cn.gov.cn.msfqt.cn http://www.morning.xbmwh.cn.gov.cn.xbmwh.cn http://www.morning.jgncd.cn.gov.cn.jgncd.cn http://www.morning.mwzt.cn.gov.cn.mwzt.cn http://www.morning.fywqr.cn.gov.cn.fywqr.cn http://www.morning.mpszk.cn.gov.cn.mpszk.cn http://www.morning.xxgfl.cn.gov.cn.xxgfl.cn http://www.morning.rwmft.cn.gov.cn.rwmft.cn http://www.morning.jpfpc.cn.gov.cn.jpfpc.cn http://www.morning.fxzw.cn.gov.cn.fxzw.cn http://www.morning.wmqrn.cn.gov.cn.wmqrn.cn http://www.morning.zkdmk.cn.gov.cn.zkdmk.cn http://www.morning.jgcyn.cn.gov.cn.jgcyn.cn