• 周一. 8月 15th, 2022

5G编程聚合网

5G时代下一个聚合的编程学习网

热门标签

【Scala】07 集合

admin

11月 28, 2021

分三大类:

序列 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)
  }
}

发表回复

您的电子邮箱地址不会被公开。