一 集合
Scala的集合有三大类:序列Seq、集Set、映射Map,所有的集合都扩展自Iterable特质。对于几乎所有的集合类,Scala都同时提供了可变和不可变的版本。
可变集合可以在适当的地方被更新或扩展。这意味着可以修改,添加,移除一个集合的元素。而不可变集合类,相比之下,内容永远不会改变。不过,仍然可以模拟添加,移除或更新操作。但是这些操作将在每一种情况下都返回一个新的集合,同时使原来的集合不发生改变,所以这里的不可变并不是变量本身的值不可变,而是变量指向的那个内存地址不可变
可变集合和不可变集合,一般可以根据集合所在包名进行区分:
-
scala.collection.immutable
-
scala.collection.mutable
1 数组
不可变数组
严格意义上数组不是集合,是一种组织和管理数据的结构,但scala中给数组一个特定的类型:Array
基本操作:创建,访问,遍历
def main(args: Array[String]): Unit = {
val array = new Array[String](3)
array.update(1,"a")
for(i <- array){
println(i)
}
反编译后的代码如下,所以构建scala中的数组,其实等同于构造java中的数组
String[] array = new String[]{null, "a", null};
赋值
可以根据索引访问数组的元素update(1,"a")
,但scala中的中括号表示泛型,所以不能在索引操作中使用,使用小括号即可array(1)="a"
,这两种赋值方式完全相同
创建数组的同时进行初始化,使用集合的伴生对象创建对象,同时进行初始化
val array = Array(1,2,3,4)
val array1 = Array.apply(1,2,3,4)
反编译后的代码为
int[] var10000 = new int[]{1, 2, 3, 4};
访问
集合分为可变集合和不可变集合,scala默认提供的集合都是不可变的,每一次添加操作都会产生新的集合
def main(args: Array[String]): Unit = {
val array = Array(1,2,3,4)
val ints = array.+:(5)
val ints1 = array.:+(5)
println(array eq ints) //false
println(array eq ints1) //false
println(ints eq ints) //false
for(i <- array){
println(i) //1,2,3,4
}
查看ints,ints1,mkString
,生成字符串,第一次遍历使用 ,隔开,第二次遍历使用-隔开
println(ints.mkString(",")) //5,1,2,3,4
println(ints1.mkString("-")) //1-2-3-4-5
如果运算符以冒号结尾,则运算规则从后向前计算
val ints = array.+:(5) //等同于
val ints = 5 +: array
val ints1 = array.:+(5) //等同于
val ints1 = array :+ 5
val ints2 = array ++ array1
println(ints2.mkString(",")) //1,2,3,4,5,6,7,8
遍历
foreach是一个循环的方法,需要传递一个参数,参数的类型是函数类型:Int => U
def foreachFunction(num:Int): Unit ={
println(num)
}
array.foreach(foreachFunction) //1,2,3,4
array将每个数字传递给函数的num,foreach循环调用foreachFunction
使用匿名函数代替声明的函数
array.foreach((num:Int)=>{println(num)})
使用函数至简原则进行化简
array.foreach(println(_))
其他操作
多维数组
var myMatrix = Array.ofDim[Int](3,3)
myMatrix.foreach(list=>println(list.mkString(",")))
输出内容为
0,0,0
0,0,0
0,0,0
合并数组
val arr3: Array[Int] = Array.concat(arr1, arr2)
arr3.foreach(println)
创建指定范围的数组,左闭右开
val arr4: Array[Int] = Array.range(0,2)
arr4.foreach(println)
创建数组并按照特定值(-1)填充指定数量的数组
val arr5:Array[Int] = Array.fill[Int](5)(-1)
arr5.foreach(println)
可变数组
直接在原数组的基础上进行修改,内容可变
def main(args: Array[String]): Unit = {
val buffer = new ArrayBuffer[Int]()
println(buffer) //ArrayBuffer()
buffer.append(1,2,3,4) //ArrayBuffer(1, 2, 3, 4)
buffer.appendAll(Array(1,2,3)) //ArrayBuffer(1, 2, 3, 4, 1, 2, 3)
buffer.insert(1,5) //ArrayBuffer(1, 5, 2, 3, 4, 1, 2, 3)
//buffer.insert(100,1) //IndexOutOfBoundsException
buffer.update(0,10) //ArrayBuffer(10, 5, 2, 3, 4, 1, 2, 3)
buffer(0) = 9 //ArrayBuffer(9, 5, 2, 3, 4, 1, 2, 3)
buffer.remove(0) //ArrayBuffer(5, 2, 3, 4, 1, 2, 3)
buffer.remove(0,3) //ArrayBuffer(4, 1, 2, 3)
println(buffer)
// 使用 ++= 运算符会更新之前的集合,不会产生新的数组
val buffer5: ArrayBuffer[Int] = buffer1 ++= buffer2
}
注意以下方法会产生新的数组
val ints = buffer - 4
println(ints eq buffer) //false
println(ints) //ArrayBuffer(1, 2, 3)
println(buffer) //ArrayBuffer(4, 1, 2, 3)
// 使用 ++ 运算符会产生新的集合数组
val buffer4: ArrayBuffer[Int] = buffer1 ++ buffer2
使用伴生对象,在创建数组的同时进行初始化
val buffer = ArrayBuffer(1,2,3,4)
不可变数组与可变数组的相互转换
def main(args: Array[String]): Unit = {
val buffer = ArrayBuffer(1,2,3,4)
val array = Array(4,5,6,7)
// 将不可变数组转换为可变数组
val buffer1: mutable.Buffer[Int] = array.toBuffer
// 将可变数组转换为不可变数组
val array1: Array[Int] = buffer.toArray
}
java集合和scala集合间的互相转换
//java ==> scala
import scala.collection.JavaConverters._
val list = new java.util.ArrayList()
list.asScala.foreach(println)
//scala ==> java
val java = List(1,2,3,4).asJava
2 集合提供的方法
scala关注的重点在于集合作为一个容器是如何对数据进行操作的,不关心底层是如何存储,管理数据的,是数组,List,Set,Map区别不大,所以重点集中到提供的几个功能
常用的方法
println(array.size) //6
println(array.length) //6
println(array.isEmpty) //false
array.contains(println(3)) // 3
println(array.distinct.mkString(",")) //1,2,3,4,5
println(array.reverse.mkString(",")) //5,4,4,3,2,1
遍历相关
println(array.mkString(","))
array.foreach(println)
array.iterator
从集合获取部分元素
val buffer = ArrayBuffer(1,2,3,4)
println(buffer.head) //1
println(buffer.tail) //ArrayBuffer(2, 3, 4)
println(buffer.tails) //<iterator>
println(buffer.last) //4
println(buffer.init) //ArrayBuffer(1, 2, 3)
println(buffer.inits) //<iterator>
//取前几个
println(buffer.take(3)) //ArrayBuffer(1, 2, 3)
println(buffer.takeRight(2)) //ArrayBuffer(3, 4)
println(buffer.drop(1)) //ArrayBuffer(2, 3, 4)
println(buffer.dropRight(1)) //ArrayBuffer(1, 2, 3)
与计算相关的方法
println(array.sum) //10
println(array.max) //4
println(array.min) //1
println(array.product) //24
如果没有现成提供的方法,需要自定义数据操作的方法,集合的数据无论有多少,最基本的数据操作其实都是两两计算
map ==> reduce ==> 简化,规约,集合
println(array.reduce((x,y) => x - y)) //-8
println(array.reduce(_ - _)) //-8
println(array.reduceLeft(_-_)) //-8
println(array.reduceRight(_-_)) //-2
reduceLeft从左边加括号:(((1 - 2)- 3)- 4)
reduceRight从右边加括号:(1 -(2 -(3 - 4)))
与集合外部的数据进行操作
val array = ArrayBuffer(1,2,3,4)
val num = 5
println(array.fold(num)(_ - _)) //-5,先执行5 - 1-->5,1,2,3,4,从左边加括号
println(array.foldLeft(num)(_-_)) //-5,将5放在左边,从左边加括号
println(array.foldRight(num)(_-_)) //3,将5放在右边,从右边加括号
//将两两计算的临时结果保留下来(检验)
println(array.scan(num)(_ - _)) //ArrayBuffer(5, 4, 2, -1, -5)
println(array.scanRight(num)(_ - _)) //ArrayBuffer(3, -2, 4, -1, 5)
功能函数
由集合对象提供函数执行自定义的功能
将数组整体乘n倍:map => 映射(转换) => 通过K可以得到V
map方法需要传递一个参数,参数的类型为函数类型:Int => B
提供的功能就是给一个a可以经过运算变为b
def main(args: Array[String]): Unit = {
val array = ArrayBuffer(1,2,3,4)
def mapFunction( num:Int): Int = {
num * 2
}
println(array.map(mapFunction))
}
使用匿名函数
println(array.map(
(num:Int) => {
num * 2
}
))
至简原则
println(array.map(_ * 2))
将整体拆分成个体的操作,如将一个二维数组拆分成一个一维数组,称为扁平化,默认只对最外层进行操作
def main(args: Array[String]): Unit = {
val array = ArrayBuffer(
ArrayBuffer(
ArrayBuffer(1,2),ArrayBuffer(5,6)
),ArrayBuffer(
ArrayBuffer(3,4),ArrayBuffer(7,8)
)
)
println(array.flatten.flatten)
}
如果需要自定义扁平化过程,使用flatMap方法
将以下单词分开
val array = Array(
"hello scala","helo hadoop"
)
println(array.flatten.mkString(","))
println(array.flatMap(
str => str.split(" ")
).mkString(","))
filter方法可以对集合中的每一条数据进行筛选过滤
满足条件的数据保留,不满足条件的数据丢弃
按照偶数过滤,是偶数留下,不是过滤掉
def main(args: Array[String]): Unit = {
val array = ArrayBuffer(1,2,3,4)
val r = array.filter(
num => {
num % 2 == 0
}
)
println(r)
val rr = array.filter(_ % 2 == 0)
println(rr)
}
groupBy方法可以根据指定的规则对每一条数据进行分组
按照奇偶数进行分组
def main(args: Array[String]): Unit = {
val array = ArrayBuffer(1,2,3,4)
val r = array.groupBy(
num => {
if (num % 2 == 0)
"偶数"
else
"奇数"
}
)
println(r)
println(array.groupBy(_ % 2 == 0))
println(array.groupBy(_ % 2))
将相同首字母的单词放到一组
val arrayBuffer = ArrayBuffer(
"hello","scala","hadoop","spark"
)
println(arrayBuffer.groupBy(_.substring(0, 1)))
sortBy方法:通过指定的规则对每一条数据进行排序处理,默认为升序
将数字按照数字的规则进行排序
val array = ArrayBuffer(1,3,4,2)
println(array.sortBy(
num => num
))
将字符串分别按照字典序和数字的规则进行排序
val array = ArrayBuffer("1","11","2","22","3")
println(array.sortBy(num => num))
println(array.sortBy(num => num.toInt))
按照降序排序
println(array.sortBy(num => num.toInt)(Ordering.Int.reverse))
完成WordCount
- 读取文件,获取原始数据,获取到的是一行一行的数据(eg. hello scala)
- 将原始数据切分成一个一个的单词(eg. hello,scala)
- 对分词的结果进行分组操作,相同的单词放到一起(eg. hello,hello => {hello => List(hello,hello)}
- 对分组后的数据进行数量统计(eg.{hello => List(hello,hello)} => {hello,2})
- 将统计结果打印在控制台
def main(args: Array[String]): Unit = {
//获取数据,将每一行的数据分别存储到一个数组中,关闭资源
val source: BufferedSource = Source.fromFile("data/word.txt")
val lines: Array[String] = source.getLines().toArray
source.close()
//拆分
val words: Array[String] = lines.flatMap(
line => line.split(" ")
)
//分组
val wordGroup: Map[String, Array[String]] = words.groupBy(word => word)
//数量统计,如果数据在转换时,无需对key进行操作,只对v进行处理,可以使用mapValues方法
val wordCount: Map[String, Int] = wordGroup.mapValues(
v => v.size
)
println(wordCount)
}
升级版
def main(args: Array[String]): Unit = {
val source: BufferedSource = Source.fromFile("data/word.txt")
val lines: Array[String] = source.getLines().toArray
source.close()
val wordCount = lines
.flatMap(_.split(" "))
.groupBy(word => word)
.mapValues(_.size)
println(wordCount)
}