集合简介
说明:
- Scala的集合有三大类:序列Seq、集Set、映射Map,所有的集合都扩展自Iterable特质。
- 对于几乎所有的集合类,Scala都同时提供了可变和不可变的版本,分别位于以下两个包 不可变集合:scala.collection.immutable 可变集合: scala.collection.mutable
不可变集合继承图
- Set、Map是Java中也有的集合
- Seq是Java没有的,我们发现List归属到Seq了,因此这里的List就和Java不是同一个概念了
- 我们前面的for循环有一个 1 to 3,就是IndexedSeq下的Vector
- String也是属于IndexeSeq
- 我们发现经典的数据结构比如Queue和Stack被归属到LinerSeq
- 大家注意Scala中的Map体系有一个SortedMap,说明Scala的Map可以支持排序
- IndexSeq和LinearSeq的区别: 1.IndexSeq是通过索引来查找和定位,因此速度快,比如String就是一个索引集合,通过索引即可定位 2.LineaSeq是线型的,即有头尾的概念,这种数据结构一般是通过遍历来查找
可变集合继承图
集合操作的通用方法:
- 带 与带-的区别: 带 是添加元素 带-是删除元素
- 一个 /-与两个 /-的区别 一个 /-是添加/删除单个元素 两个 /-是添加/删除一个集合所有元素
- 冒号在前、冒号在后、不带冒号的区别 冒号在前是将元素添加到集合末尾 冒号在后是将元素添加到集合最前面 不带冒号是将元素添加到集合末尾
- 带=与不带=的区别 带=是修改集合本身 不带=是生成一个新集合,原集合没有改变
- update与updated的区别: update是修改集合本身 updated是生成一个新集合,原集合没有改变
集合常用的方法
以 List
作为参考
scala> list.
flatMap min sortBy
: flatten minBy sortWith
: fold mkString sorted
/: foldLeft nonEmpty span
: foldRight orElse splitAt
:: forall padTo startsWith
::: foreach par stringPrefix
: genericBuilder partition sum
WithFilter groupBy patch tail
addString grouped permutations tails
aggregate hasDefiniteSize prefixLength take
andThen hashCode product takeRight
apply head productArity takeWhile
applyOrElse headOption productElement to
canEqual indexOf productIterator toArray
collect indexOfSlice productPrefix toBuffer
collectFirst indexWhere reduce toIndexedSeq
combinations indices reduceLeft toIterable
companion init reduceLeftOption toIterator
compose inits reduceOption toList
contains intersect reduceRight toMap
containsSlice isDefinedAt reduceRightOption toSeq
copyToArray isEmpty repr toSet
copyToBuffer isTraversableAgain reverse toStream
corresponds iterator reverseIterator toString
count last reverseMap toTraversable
diff lastIndexOf reverse_::: toVector
distinct lastIndexOfSlice runWith transpose
drop lastIndexWhere sameElements union
dropRight lastOption scan unzip
dropWhile length scanLeft unzip3
endsWith lengthCompare scanRight updated
equals lift segmentLength view
exists map seq withFilter
filter mapConserve size zip
filterNot max slice zipAll
find maxBy sliding zipWithIndex
- 获取集合长度
val list=List(1,2,3,4,5,6,7,8,9,10)
length
println(s"length=${list.length}")
length=10
size
println(s"size=${list.size}")
size=10
- 判断集合是否为空(
isEmpty
)
val list=List(1,2,3,4,5,6,7,8,9,10)
val list2=List()
代码语言:javascript复制println(list.isEmpty) // false
代码语言:javascript复制println(list2.isEmpty) // true
- 判断是否包含某个元素(
contains
)
val list=List(1,2,3,4,5,6,7,8,9,10)
代码语言:javascript复制println(list.contains(5)) // true
代码语言:javascript复制println(list.contains(100)) // false
- 获取集合元素组成的字符串(
mkString
)
val list=List(1,2,3,4,5,6,7,8,9,10)
mkString(分隔符)
代码语言:javascript复制println(list.mkString(","))
1,2,3,4,5,6,7,8,9,10
- 元素遍历
val list=List(1,2,3,4,5,6,7,8,9,10)
for(e <- list){
println(s"e=$e")
}
代码语言:javascript复制e=1
e=2
e=3
e=4
e=5
e=6
e=7
e=8
e=9
e=10
- 迭代器
val list=List(1,2,3,4,5,6,7,8,9,10)
// 获得一个迭代器
val iterator: Iterator[Int] = list.iterator
// 判断是否有下一个元素
while(iterator.hasNext){
// 取值
println(s"next=${iterator.next()}")
}
代码语言:javascript复制next=1
next=2
next=3
next=4
next=5
next=6
next=7
next=8
next=9
next=10
除了使用while
也可以使用for
val iterator: Iterator[Int] = list.iterator
for (it<-iterator){
println(it)
}
衍生集合
- 去重(
distinct
)
val list=List(1,2,3,44,3,5,3,1,2)
//去重
val newList: List[Int] = list.distinct
代码语言:javascript复制println(newList)
List(1, 2, 3, 44, 5)
- 获取除开前N个元素的所有元素(
drop
)
val list=List(1,2,3,44,3,5,3,1,2)
// 弹出前三个元素
val newList: List[Int] = list.drop(3)
代码语言:javascript复制println(newList)
List(44, 3, 5, 3, 1, 2)
- 获取除开后N个元素的所有元素(
dropRight
)
val list=List(1,2,3,44,3,5,3,1,2)
val newList: List[Int] = list.dropRight(3)
代码语言:javascript复制println(newList)
List(1, 2, 3, 44, 3, 5)
- 获取第一个元素(
head
)
val list=List(1,2,3,44,3,5,3,1,2)
val value: Int = list.head
代码语言:javascript复制println(value) //1
- 获取最后一个元素
val list=List(1,2,3,44,3,5,3,1,2)
val value: Int = list.last
代码语言:javascript复制println(value) // 2
- /获取除开最后一个元素的其他所有元素(
init
)
val list=List(1,2,3,44,3,5,3,1,2)
val newList: List[Int] = list.init
代码语言:javascript复制println(newList)
List(1, 2, 3, 44, 3, 5, 3, 1)
- 获取除开第一个元素的其他所有元素(
tail
)
val list=List(1,2,3,44,3,5,3,1,2)
val newList: List[Int] = list.tail
代码语言:javascript复制println(newList)
List(2, 3, 44, 3, 5, 3, 1, 2)
- 反转(
reverse
)
val list=List(1,2,3,44,3,5,3,1,2)
println(list.reverse)
代码语言:javascript复制List(2, 1, 3, 5, 3, 44, 3, 2, 1)
- 获取指定角标范围的子集合 [包前不包后](
slice
)
val list=List(1,2,3,4,5,6,7,8)
val newList: List[Int] = list.slice(2, 5)
代码语言:javascript复制println(newList)
List(3, 4, 5)
- 滑窗(
sliding
) size: 窗口的长度 step: 滑动的长度
val list=List(1,2,3,4,5,6,7,8)
//滑窗
val iterator: Iterator[List[Int]] = list.sliding(2)
for (it <-iterator){
println(it)
}
代码语言:javascript复制List(1, 2)
List(2, 3)
List(3, 4)
List(4, 5)
List(5, 6)
List(6, 7)
List(7, 8)
若不指定 step
默认长度为1,当然我们可以自定义step
val list=List(1,2,3,4,5,6,7,8)
//滑窗
val iterator: Iterator[List[Int]] = list.sliding(3,2)
for (it <-iterator){
println(it)
}
代码语言:javascript复制List(1, 2, 3)
List(3, 4, 5)
List(5, 6, 7)
List(7, 8)
注意 step
超过 size
会造成数据丢失
val list=List(1,2,3,4,5,6,7,8)
//滑窗
val iterator: Iterator[List[Int]] = list.sliding(2,3)
for (it <-iterator){
println(it)
}
代码语言:javascript复制List(1, 2)
List(4, 5)
List(7, 8)
- 获取前N个元素(
take
)
val list=List(1,2,3,4,5,6,7,8)
val newList: List[Int] = list.take(3)
代码语言:javascript复制println(newList)
List(1, 2, 3)
- 获取后N个元素(
takeRight
)
val list=List(1,2,3,4,5,6,7,8)
val newList: List[Int] = list.takeRight(3)
代码语言:javascript复制println(newList)
List(6, 7, 8)
- 交集 [取两个集合共同的元素]('intersect')
val list1=List(1,2,3,4,5,6,7,8)
val list2=List(5,6,7,8,9,10)
val newList: List[Int] = list1.intersect(list2)
代码语言:javascript复制println(newList)
List(5, 6, 7, 8)
- 差集 [A差B的结果就是取A中有B中没有的元素](
diff
)
val list1=List(1,2,3,4,5,6,7,8)
val list2=List(5,6,7,8,9,10)
val newList: List[Int] = list1.diff(list2)
代码语言:javascript复制println(newList)
List(1, 2, 3, 4)
- 并集(
union
)
val list1=List(1,2,3,4,5,6,7,8)
val list2=List(5,6,7,8,9,10)
val newList: List[Int] = list1.union(list2)
代码语言:javascript复制println(newList)
List(1, 2, 3, 4, 5, 6, 7, 8, 5, 6, 7, 8, 9, 10)
- 拉链(
zip
),两个集合之间元素交叉
val list1=List("张三","李四","王五")
val list2=List("阿娇","糖心","绣花")
val newList: List[(String, String)] = list1.zip(list2)
代码语言:javascript复制println(newList)
List((张三,阿娇), (李四,糖心), (王五,绣花))
有一天王五分手了,没有了女朋友,张三和李四就不叫王五玩了。
代码语言:javascript复制val list1=List("张三","李四","王五")
val list2=List("阿娇","糖心")
val newList: List[(String, String)] = list1.zip(list2)
使用拉链
,必须保证元素之间能够交叉。
println(newList)
List((张三,阿娇), (李四,糖心))
- 反拉链(
unzip
)
val list1=List("张三","李四","王五")
val list2=List("阿娇","糖心")
val newList: List[(String, String)] = list1.zip(list2)
代码语言:javascript复制println("反拉链:" newList.unzip)
println("拉链:" newList)
代码语言:javascript复制反拉链:(List(张三, 李四),List(阿娇, 糖心))
拉链:List((张三,阿娇), (李四,糖心))
- 将元素与角标拉链(
zipWithIndex
),将返回一个角标。
println("反拉链:" newList.unzip)
println("角标拉链:" newList.zipWithIndex)
println("拉链:" newList)
代码语言:javascript复制反拉链:(List(张三, 李四),List(阿娇, 糖心))
角标拉链:List(((张三,阿娇),0), ((李四,糖心),1))
拉链:List((张三,阿娇), (李四,糖心))
集合初级计算函数
- 获取最大值(
max
)
val list=List[Int](23,1,2,34,5432,22)
代码语言:javascript复制println(list.max) // 5432
- 获取最小值(
min
)
val list=List[Int](23,1,2,34,5432,22)
代码语言:javascript复制println(list.min) // 1
- 根据指定字段获取最大值(
maxBy
) maxBy(func: 集合元素类型 => B) maxBy里面的函数是针对集合每个元素进行操作 maxBy是根据函数的返回值获取最大元素
val list2=List[(String,Int)](
("张三",19),
("春娇",18),
("牛二娃",8),
("刘大叔",39),
("李二婶",42),
("李四",19),
)
代码语言:javascript复制println(list2.maxBy(_._2))
(李二婶,42)
- 根据指定字段获取最小值 minBy(func: 集合元素类型 => B ) minBy里面的函数是针对集合每个元素进行操作 minBy后续就是根据函数的返回值获取最小值
val list2=List[(String,Int)](
("张三",19),
("春娇",18),
("牛二娃",8),
("刘大叔",39),
("李二婶",42),
("李四",19),
)
代码语言:javascript复制println(list2.minBy(_._2))
(牛二娃,8)
- 求和
val list=List[Int](23,1,2,34,5432,22)
代码语言:javascript复制println(list.sum) // 5514
- 排序
sorted
sortBy(func: 集合元素类型 => B)
sortBy里面的函数也是针对集合每个元素进行操作
sortBy后续是根据函数返回值进行排序
直接根据元素本身排序[默认升序]
代码语言:javascript复制val list=List[Int](23,1,2,34,5432,22)
代码语言:javascript复制println(list.sorted)
List(1, 2, 22, 23, 34, 5432)
根据指定字段排序【默认升序】
代码语言:javascript复制val list=List[Int](23,1,2,34,5432,22)
自定义 Ordering
特质
val ordered=new Ordering[Int]{
override def compare(x: Int, y: Int) = {
if(x<y) 1
else if(x==y) 0
else -1
}
}
代码语言:javascript复制println(list.sorted(ordered))
List(5432, 34, 23, 22, 2, 1)
sortWith
[重点]
sortWith(func: (集合元素类型,集合元素类型) => Boolean )
sortWith 中的函数如果第一个参数>第二个参数,降序
sortWith 中的函数如果第一个参数<第二个参数,升序
根据指定规则排序【指定升序或者降序】
代码语言:javascript复制val list2=List[(String,Int)](
("张三",19),
("春娇",18),
("牛二娃",8),
("刘大叔",39),
("李二婶",42),
("李四",19),
)
按年龄降序
代码语言:javascript复制println(list2.sortWith(_._2 > _._2))
List((李二婶,42), (刘大叔,39), (张三,19), (李四,19), (春娇,18), (牛二娃,8))
按年龄升序
代码语言:javascript复制println(list2.sortWith(_._2 < _._2))
List((牛二娃,8), (春娇,18), (张三,19), (李四,19), (刘大叔,39), (李二婶,42))
高阶函数
map
map(func: 集合元素类型 => B ): 映射
map里面的函数是针对集合每个元素进行计算,计算完成之后会返回一个结果
map使用场景: 一般用于一对一,主要用于数据的类型/值的转换
案例:统计集合中字符串的个数并返回
代码语言:javascript复制val list=List("java","python","scala","hadoop","hive")
代码语言:javascript复制 //映射
val mapList: List[String] = list.map(s=>s"$s:${s.length}")
代码语言:javascript复制println(mapList.mkString(","))
java:4,python:6,scala:5,hadoop:6,hive:4
flatten
flatten只能压平第二层集合
flatten的应用场景: 一对多,只能用于集合嵌套集合的数据类型
val list2=List[List[String]](
List[String]("java","String","main"),
List[String]("hadoop","hdfs","yarn"),
List[String]("hive","python"),
)
代码语言:javascript复制println(s"不使用平铺化= ${list2.mkString(",")}")
不使用平铺化= List(java, String, main),List(hadoop, hdfs, yarn),List(hive, python)
代码语言:javascript复制val flattenList= list2.flatten
println(s"使用平铺化= ${flattenList.mkString(",")}")
使用平铺化= java,String,main,hadoop,hdfs,yarn,hive,python
flatMap
先进行map
后进行 flatten
flatMap(func: 集合元素类型 => 集合)
flatMap里面的函数也是针对集合每个元素操作
flatMap的使用场景: 一对多
flatMap与flatten的区别: flatMap是先对数据转换再压平, flatten只是单纯的压平
案例:将句子转成单词
代码语言:javascript复制val list3=List("hello spark hello java","hello hadoop spark","spark hadoop java")
代码语言:javascript复制val newList: List[String] = list3.flatMap(work => {
// 按照空格切分
work.split(" ")
})
代码语言:javascript复制println(newList.mkString(","))
hello,spark,hello,java,hello,hadoop,spark,spark,hadoop,java
foreach
foreach(func: 集合元素类型 => B):Unit
foreach与map类似,唯一的区别在与map计算之后会生成一个新集合,foreach没有返回值
foreach相当于普通for循环, map相当于有yield表达式的for循环
集合遍历
代码语言:javascript复制val list=List("java","python","scala","hadoop","hive")
打印输出
代码语言:javascript复制list.foreach(s=>println(s))
代码语言:javascript复制java
python
scala
hadoop
hive
filter
filter( func: 集合元素类型 => Boolean ): 根据指定的规则过滤
filter里面的函数也是针对集合每个元素进行操作
filter保留的是函数返回值为true的数据
案例:剔除集合中java 字符串
代码语言:javascript复制val list4=List("hello","spark","hello","java","hello","hadoop","spark","spark","hadoop","java")
代码语言:javascript复制val newList4: List[String] = list4.filter(s => s.ne("java"))
代码语言:javascript复制println(newList4 )
List(hello, spark, hello, hello, hadoop, spark, spark, hadoop)
reduce
reduce(func: (集合元素类型,集合元素类型) => 集合元素类型): 从左向右聚合
reduce中函数在第一次计算的时候,函数第一个参数的值 = 集合第一个元素
reduce中函数在第N次计算的时候,函数第一个参数的值 = N-1次的计算结果
案例:汇总
代码语言:javascript复制val list5=List[Int](23,1,2,34,5432,22)
代码语言:javascript复制val sum=list5.reduce((x,y)=>{x y})
代码语言:javascript复制println(sum) // 5514
reduceLeft
和 reduce
一样 从左向右聚合
val sum=list5.reduceLeft((x,y)=>{x y})
代码语言:javascript复制println(sum) // 5514
reduceRight
reduceRight(func: (集合元素类型,集合元素类型) => 集合元素类型): 从右向左聚合
reduceRight中函数在第一次计算的时候,函数第二个参数的值 = 集合最后一个元素
reduceRight中函数在第N次计算的时候,函数第二个参数的值 = N-1次的计算结果
减法 从 左到右运行
代码语言:javascript复制val value1=list5.reduceLeft((x,y)=>{x-y})
println(value1) // -5468
减法 从 右到到左运行
代码语言:javascript复制val value2=list5.reduceRight((x,y)=>{x-y})
println(value2) // 5400
reduce*Option
将结果保存到Option
中
fold
fold(默认值: 集合元素类型)(func: (集合元素类型,集合元素类型)=>集合元素类型):从左向右聚合
fold中的函数在第一次计算的时候,函数第一个参数的值 = 默认值 【与reduce区别的部分】
fold中的函数在第N次计算的时候,函数第一个参数的值 = N-1次的计算结果
val list6 = List(1,2,3)
代码语言:javascript复制val functionToInt = list6.fold(100)((x,y)=>x-y)
代码语言:javascript复制println(functionToInt) // 94
foldRight
foldRight(默认值: B)( func: (集合元素, B) => B ): 从右向左计算
foldRight中的函数在第一次计算的时候,函数第二个参数的值 = 默认值
foldRight中的函数在第N次计算的时候,函数第二个参数的值 = N-1次的计算结果
val list6 = List(1,2,3)
代码语言:javascript复制val functionToInt = list6.foldRight(100)((x,y)=>x-y)
代码语言:javascript复制println(functionToInt) // -98