【Scala】07 集合
分三大类:
序列 Seq
集 Set
映射 Map
所有集合类型都扩展自Iterable特质(可迭代的)
所有集合类型都提供【可变】和【不可变】的版本
归纳在下面两个包中
scala.collections.immutable 不可变的
意思这个集合对象不可改变,每一次修改即返回新的对象,不对原始集合对象修改
等同java.lang.String
scala.collections.mutable 不可变的
意思这个集合对象可改变,每一次修改即返回原始的对象,等同java.lang.StringBuilder
固定数组Array的操作:
package cn object HelloScala { def main(args: Array[String]): Unit = { // 推倒类型变量, new 新创建 Array表示数组 [String]表示数组元素的类型 (10) 表示数组的长度 val strings = new Array[String](10) // 数组的长度属性 val len = strings.length // 对指定索引上的元素修改赋值,也可以用来读取 strings(0) = "aaa" // 创建方式2 val arr1 = Array.apply(10, 20, 30, 40) val arr2 = Array(10, 20, 30, 40) // 直接Array就是apply的缩写 // 遍历 // for(el <- 0 to arr1.length - 1) // for(el <- 0 until arr1.length) // for(el <- arr1.indices) // for(el <- arr1) for(el <- 0 until arr1.length) println(el) // 迭代遍历 val itr = arr2.iterator while (itr.hasNext) println(itr.next()) // forEach方法 arr1.foreach(el => { println(el) }) arr2.foreach(println) // 添加元素 val newArr1 = arr1.:+(123) // 不可变数组添加元素是一个新的数组 :+(element) 在末尾加 val newArr2 = newArr1.+:(123) // +:(element) 在开头添加 // 使用这种添加方式 val newArr3 = newArr2 :+ 16 // 对象(参数) val newArr4 = 16 +: newArr2// (参数) 对象 // 简洁明了的添加元素操作 val newArr5 = 10 +: 12 +: 33 +: newArr4 :+ 2 :+ 11 :+ 5; // } }
添加元素的方法:
package cn import scala.collection.mutable.ArrayBuffer object HelloScala { def main(args: Array[String]): Unit = { // 缓冲数组 val value = new ArrayBuffer[Any] // 不要这样赋值 会索引越界, 因为value里面什么都没有,需要存在确定的元素才可以这样去访问 value(0) = 100 // 访问元素和固定数组是一样的 // 添加元素 value += 10 // 使用的是直接加等于 77 +=: value // value.append(100) append方法被移除了? value.prepend(12, 22, 33) // prepend方法标记为过时的 value.insert(1, 0) value.insertAll(1, value) value.prependAll(value) } }
固定List :
package cn import scala.collection.mutable import scala.collection.mutable.ArrayBuffer object HelloScala { def main(args: Array[String]): Unit = { // 创建固定List val list = List(false, 10, "string") // 能装填不同数据类型的元素 println(list) // List(false, 10, string) println(list(1)) // 可以读取 // list(1) = 10 // 但是无法写入 // 遍历List的每一个元素 list.foreach(println) // 添加元素 因为是固定的对象,所以添加元素就必须要返回新的对象 val l2 = list.+:(25) // 从头部添加 val l3 = list :+ 25 // 从尾部添加 println(l2) println(l3) val l4 = l3.::(50) // 从头部添加 println(l4) println(Nil) // Nil 是一个空List对象 val l5 = 40 :: Nil // 参数写前面,对象调用写后面的语法 // 所以就有像这种数组的声明方式 val l6 = 100 :: 80 :: 60 :: 40 :: 20 :: Nil println(l6) // List(100, 80, 60, 40, 20) val l7 = l6 :: l6 // 该方法会把List对象做为元素也装进这个List对象中 println(l7) // List(List(100, 80, 60, 40, 20), 100, 80, 60, 40, 20) // 如果需要装填的是里面得元素 则使用三冒号实现 val l8 = l6 ::: l6 println(l8) // 或者使用这个符号 ++ 作用和三冒号是一样的 val l9 = l6 ++l8 } }
可变List:
package cn import scala.collection.mutable import scala.collection.mutable.{ArrayBuffer, ListBuffer} object HelloScala { def main(args: Array[String]): Unit = { // 可变list // 创建 val lb1 : ListBuffer[Int] = new ListBuffer[Int]() // 方式1 val lb2 = ListBuffer(12, 30, 50) // 方式2 (推荐这种伴生对象实现) // 添加元素 lb1.append(112) // 后置追加 lb2.prepend(122) // 前置追加 lb1.insert(0, 123) // 指定索引位置追加 教程上可以追加多个,但是这个版本好像不支持了 // +=: 表示前置追加, += 表示后置追加 31 +=: 33 +=: lb1 += 12 += 35 // 合并两个List并且返回一个新的List对象 val lb3 = lb1 ++ lb2 // 把lb2的元素合并到lb1中 lb1 ++= lb2 // 反过来lb1的元素合并到lb2中 lb1 ++=: lb2 // 和固定List不一样,ListBuffer是支持修改的 lb1(1) = 100 } }
不可变Set & 可变Set
package cn import scala.collection.mutable import scala.collection.mutable.{ArrayBuffer, ListBuffer} object HelloScala { def main(args: Array[String]): Unit = { // Set会覆盖重复的元素 不可变类型 val set = Set(12, 33, 44, 44) // 添加元素 val set2 = set.+(11) val set3 = set2 + 22 // 合并Set对象 val set4 = set2 ++ set3 // 删除元素 val set5 = set4 - 100 // 可变Set val mtbSet = mutable.Set(10, 10, 40) mtbSet + 33 // 推荐直接调用方法 mtbSet += 45 mtbSet.add(45) // 返回添加结果 mtbSet -= 45 mtbSet.remove(45) // 返回添加结果 // 合并Set // 合并两个Set返回到新Set对象中 val mtbSet1 = mtbSet ++ mtbSet // 合并到 Set1对象数组 mtbSet ++= mtbSet } }
不可变Map
package cn import scala.collection.mutable import scala.collection.mutable.{ArrayBuffer, ListBuffer} object HelloScala { def main(args: Array[String]): Unit = { // 创建Map // 支持多个键值对创建 val map : Map[String, Integer] = Map( "A" -> 0, "B" -> 12, "C" -> 33 ) println(map) // Map(A -> 0, B -> 12, C -> 33) println(map.getClass) // class scala.collection.immutable.Map$Map3 // 遍历,元素是每一个键值对 map.foreach(println) // 扩展 map.foreach( (keyValue : (String, Integer)) => println(keyValue) ) // 按Key遍历 for(key <- map.keys) { println(s"key : $key & value : ${map.get(key)}") } // 要直接获取Value -> map.get(Key).get for(key <- map.keys) { println(s"key : $key & value : ${map.get(key).get}") } // 如果Value是空的,可能发生异常,采用安全的获取方法是 map.getOrElse(key, defaultValue) 设置缺省值 for(key <- map.keys) { println(s"key : $key & value : ${map.getOrElse(key, 0)}") } /** * key : A & value : Some(0) * key : B & value : Some(12) * key : C & value : Some(33) * * Value可能是一个NULL值,所以使用Option封装了一层 Some是一个Option子类 */ // Map 也支持像数组或者List那种方式访问值 println(map("A")) } }
可变Map和不可变Map调用的操作方法一样
多了可操作的方法
package cn import scala.collection.mutable import scala.collection.mutable.{ArrayBuffer, ListBuffer} object HelloScala { def main(args: Array[String]): Unit = { // 创建可变Map // 支持多个键值对创建 val map : mutable.Map[String, Integer] = mutable.Map( "A" -> 0, "B" -> 12, "C" -> 33 ) println(map) // Map(A -> 0, B -> 12, C -> 33) println(map.getClass) // class scala.collection.mutable.HashMap // 可变Map就支持元素的添加和移除 map.put("D", 22) // 添加元素1 map += ("E", 33) // 添加元素2 map.remove("D") // 移除元素1 map -= "E" // 移除元素2 map -= ("E", 33) // 移除元素3 // 遍历,元素是每一个键值对 map.foreach(println) val map2 : mutable.Map[String, Integer] = mutable.Map( "A" -> 0, "B" -> 12, "C" -> 33 ) map ++= map2 // 合并Map2的元素给Map(覆盖已存在的Key&Value) // 或者合并返回新的map对象 val map3 = map ++ map2 // 扩展 map.foreach( (keyValue : (String, Integer)) => println(keyValue) ) // 按Key遍历 for(key <- map.keys) { println(s"key : $key & value : ${map.get(key)}") } // 要直接获取Value -> map.get(Key).get for(key <- map.keys) { println(s"key : $key & value : ${map.get(key).get}") } // 如果Value是空的,可能发生异常,采用安全的获取方法是 map.getOrElse(key, defaultValue) 设置缺省值 for(key <- map.keys) { println(s"key : $key & value : ${map.getOrElse(key, 0)}") } /** * key : A & value : Some(0) * key : B & value : Some(12) * key : C & value : Some(33) * * Value可能是一个NULL值,所以使用Option封装了一层 Some是一个Option子类 */ // Map 也支持像数组或者List那种方式访问值 println(map("A")) } }
元组操作:
package cn object HelloScala { def main(args: Array[String]): Unit = { // 元组 通用数据类型容器,没有数据类型限制,但是最多存放22个元素? val tuple = ("String", 100, false, 3.14, "C") // 显式要求数据类型 val tuple2 : (String, Int, Boolean, Double, Char) = ("String", 100, false, 3.14, 'C') // Map的键值对就是元组,只是个数固定是2 一个Key 一个Value // 访问元组的元素可以直接调用 tuple._index println(tuple._2) // 这种方式第一个是 _1 从1开始 println(tuple2.productElement(0)) // 调用productElement(index) 是从0开始 // 使用迭代器遍历访问 for (elem <- tuple.productIterator) println(elem) // 元组支持嵌套元组,元组本身也算一种类型 val tuple3 = (1, true, tuple, tuple2) for (elem <- tuple3.productIterator) println(elem) // 同样的操作可以连续调用 println(tuple3._3._2) } }
通用集合操作:
package cn object HelloScala { def main(args: Array[String]): Unit = { // 集合类型通用操作 val list = List(1, 2, 3, 4, 5) println(list.length) // 获取长度 val set = Set(1, 2, 3, 4, 5) println(set.size) // set不是长度属性,获取的是Size大小 // 数组或者List可以直接遍历拿到元素 for(el <- list) println(el) // set没有长度一说,需要通过迭代器执行 for(el <- set.iterator) println(el) // 使用mkString转换为字符串 println(list.mkString(",")) // 判断是否包含某元素 println(set.contains(23)) } }
衍生集合操作
package cn.dzz object Main { def main(args: Array[String]): Unit = { // 衍生集合操作 val list1 = List(1, 3, 5, 7, 8, 10, 113) val list2 = List(2, 4, 5, 7, 9, 10, 11) // 获取集合的头 println(list1.head) // 获取集合的尾部 (不是头就是尾?) 意思是返回除了头部 返回后面所有的元素(新的List对象) println(list1.tail) // 获取集合的最后一个元素 println(list1.last) // 获取集合除最后一个元素的所有元素 println(list1.init) // 反转 println(list1.reverse) // 获取指定位置长度的元素1 println(list1.take(3)) // 获取前面3个元素 // 获取指定位置长度的元素2 println(list1.takeRight(3)) // 从右边开始获取3个元素 // 移除前后的元素 println(list1.drop(3)) // 从左边移除3个元素 println(list1.dropRight(3)) // 从右边移除三个元素 // 并集 A集合和B集合相同的元素原封不动 Set类会做去重处理 val unionList = list1.union(list2) val unionList2 = list1 ::: list2 // 交集 取出AB集合共同的部分 val intersectList = list1.intersect(list2) // 差集 取出AB集合所独有的部分 val diffList = list1.diff(list2) // 属于List1,但是不属于List2的元素 // 拉链 ? 将两个集合合并成一个二元组集合 println(list1.zip(list2)) // 滑窗 固定筛选长度,可以偏移量的获取集合中的元素 println(list1.sliding(3)) // 返回的是一个Iterator 默认步长是1,可调用重载 println(list1.sliding(3), 3)这样 // 获取滑窗筛选的元素 for (el <- list1.sliding(3)) println(el) } }
集合计数方法:
简单的聚合计算和排序处理:
package cn.dzz object Main { def main(args: Array[String]): Unit = { // 集合的聚合处理 val list = List(2, 42, 1213, 33, 44) // 求和 println(list.sum) // 最大值 println(list.max) val list2 = List(("A", 2), ("B", 42), ("C", 1213), ("D", 33), ("E", 44)) // 如果是复杂类型的元素。 可以重写maxBy方法来执行排序 list2.maxBy( (tuple :(String, Int)) => tuple._2) // 按照元组的第二元素类型获取最大值(作为排序的标准) list2.maxBy(_._2) // 可以缩写成这样 // 乘积 println(list.product) // 最小值 println(list.min) // 排序 println(list.sorted) // 升序排序 println(list.sorted.reverse) // 降序排序 // 复杂类型按指定元素类型排序 println(list2.sortBy(_._2)) println(list2.sortBy(_._2).reverse) // 指定排序方式 println(list.sortWith(_ > _)) // 复杂类型的话 /** * list2.sortWith( (a : (String, Int), b : (String, Int)) => { * a._2 > b._2 * }) */ println(list2.sortWith(_._2 > _._2)) // 其实像这种排序和比较大小的,在JS里面就是存在的,包括Jquery,也不是什么新鲜的东西了,语法很类似, // 只是JS还是比较强调方法,不允许这种极致的缩写语法 } }
复杂函数处理:
package cn import scala.collection.mutable object HelloScala { def main(args: Array[String]): Unit = { val list = List(1, 2, 3, 4, 6, 10, 22) // - - - - - 过滤 - - - - - // 只获取偶数元素 val evenList = list.filter(el => el % 2 == 0) // filter方法 自定义过滤的条件,函数结果必须是一个Boolean,符合条件的元素会被收集 println(evenList) // 只获取奇数 println(list.filter(_ % 2 == 1)) // - - - - - 转换 toMap - - - - - // 将List的每个元素乘2处理 println(list.map(el => el * 2)) // - - - - - 扁平化 - - - - - val nestedList : List[List[Int]] = List( List(1, 2, 3, 4, 5, 10), List(1, 2, 3, 4, 5, 9), ) // 把一个立体的数据类型合并成一个简单List对象 // 常规处理 val flatList = nestedList(0) ::: nestedList(1) println(flatList) // 直接调用已封装好的参数 val flatList2 = nestedList.flatten println(flatList2) // - - - - - 扁平化和映射 - - - - - val strList = List( "username root", "password 123456", "host 127.0.0.1", "port 3308" ) val splitList: List[Array[String]] = strList.map(str => str.split(" ")) // 分词 println(splitList) val flattenList = splitList.flatten println(flattenList) // 换成flatMap直接处理完成 val flatMapList = strList.flatMap(_.split(" ")) println(flatMapList) // - - - - - 分组 GROUP BY - - - - - val list2 = List(1, 2, 3, 4, 5, 6, 7, 8, 9) // 将List直接变成奇偶两组 val groupMap = list2.groupBy(_ % 2) println(groupMap) // HashMap(0 -> List(2, 4, 6, 8), 1 -> List(1, 3, 5, 7, 9)) Map[Int, List[Int]] val groupMap2 = list2.groupBy(data => { if (data % 2 == 0) "偶" else "奇" }) println(groupMap2) // Map[String, List[Int]] HashMap(偶 -> List(2, 4, 6, 8), 奇 -> List(1, 3, 5, 7, 9)) // 给定一组词,按首次母进行区分 val countryList = List( "China", "America", "Canada", "Japan", "India", "Australia", "Russia", ) println(countryList.groupBy(_.charAt(0))) // HashMap(J -> List(Japan), A -> List(America, Australia), I -> List(India), C -> List(China, Canada), R -> List(Russia)) // - - - - - 简化,规约 reduce - - - - - val list4 = List(1, 2, 3, 4) println(list4.reduce(_ + _)) // 10 println(list4.reduceRight(_ + _)) // 从右边开始执行 println(list4.reduceLeft(_ + _)) // 从左边开始执行 println(list4.reduce(_ - _)) // -8 println(list4.reduceRight(_ - _)) // -2 println(list4.reduceLeft(_ - _)) // 从左边开始执行 -8 // - - - - - 折叠? fold Reduce扩展 - - - - - println(list4.fold(10)(_ + _)) // 10 + 1 + 2 + 3 + 4 println(list4.foldLeft(10)(_ + _)) println(list4.foldLeft(10)(_ - _)) println(list4.foldRight(10)(_ - _)) // 1 - (2 - (3 - (4 - 10))) // - - - - - Map合并 - - - - - val map1 = Map( "A" -> 1, "B" -> 2, "C" -> 3, ) val map2 = mutable.Map( "A" -> 4, "B" -> 5, "C" -> 6, "D" -> 9, ) println(map1 ++ map2) // Map(A -> 4, B -> 5, C -> 6, D -> 9) // 要实现Value相加,而不是进行覆盖 val map3 = map1.foldLeft(map2)( (mergedMap, mp) => { val key = mp._1 val value = mp._2 mergedMap(key) = mergedMap.getOrElse(key, 0) + value mergedMap }) } }
单词计数案例
package cn object HelloScala { def main(args: Array[String]): Unit = { // 单词计数 案例 val stringList = List( "hello", "hello scala", "hello world", "hello spark", "hello flink", ) // 对字符串进行切分,得到所有单词的列表 val strList = stringList.flatMap(_.split(" ")) println(strList) // 对相同的单词进行分组处理 // println(strList.groupBy(_)) // 这里不能直接这样写,编译器报错了 /** * missing parameter type for expanded function ((<x$2: error>) => strList.groupBy(x$2)) * println(strList.groupBy(_)) */ val groupMap = strList.groupBy(el => el) println(groupMap) // 对Map中的每个Value取长度,得到每个单词的个数 val groupMap2 = groupMap.map(tp => (tp._1, tp._2.length)) println(groupMap2) // map转List 排序取前3 val sortedList = groupMap2.toList.sortWith(_._2 > _._2).take(3) println(sortedList) } }
单词计数,复杂案例:
package cn object HelloScala { def main(args: Array[String]): Unit = { // 单词计数 复杂案例 val stringList : List[(String, Int)] = List( ("hello", 1), ("hello world", 2), ("hello scala", 3), ("hello spark from scala", 4), ("hello flink from scala", 5) ) // 展开成普通的集合 val strList = stringList.map(tuple => (tuple._1.trim + " ") * tuple._2) println(strList) val finalList = strList .flatMap(_.split(" ")) .groupBy(el => el) .map(tuple => (tuple._1, tuple._2.length)) .toList .sortWith(_._2 > _._2) .take(3) println(finalList) // 方式2 直接基于预计统计的结果进行转换 val preCountList = stringList.flatMap( tuple => { val strings = tuple._1.split(" ") strings.map(word => (word, tuple._2)) }) println(preCountList) val preCountMap : Map[String, List[(String, Int)]] = preCountList.groupBy(_._1) println(preCountMap) // 这一段操作被Scala2.13版本弃用了, val countMap = preCountMap.mapValues( stringList => stringList.map(_._2).sum ) println(countMap) // 这段打印拿不到对象信息 println(countMap.toList.sortWith(_._2 > _._2).take(3)) // 但是最后调用不影响 } }
队列操作:
package cn import scala.collection.immutable.Queue import scala.collection.mutable object HelloScala { def main(args: Array[String]): Unit = { // 队列 val que = new mutable.Queue[String]() // 空字符串元素类型队列 println(que) que.enqueue( // 入队操作 "A", "B", "C", "D" ) println(que) println(que.dequeue()) // 出列操作 println(que) println(que.dequeue()) println(que) println(que.dequeue()) println(que) println(que.dequeue()) println(que) // 不可变队列 val que2:Queue[String] = Queue("C", "D", "E") val que3 = que2.enqueue("F") println(que3) } }
并行集合:
package cn import scala.collection.immutable.Queue import scala.collection.mutable object HelloScala { def main(args: Array[String]): Unit = { // 并行集合 多核优化 // val idxSeq = (1 to 100).map(i => { // s"currThreadName = ${Thread.currentThread.getName}, currThreadId = ${Thread.currentThread.getId}" // }) // println(idxSeq) // val idxSeq = (1 to 100).par.map(i => { // 这一段并行集合的已经不能使用了? // s"currThreadName = ${Thread.currentThread.getName}, currThreadId = ${Thread.currentThread.getId}" // }) // println(idxSeq) } }