【Scala】基础16--集合5

本文深入探讨Scala中的集合特性,包括如何使用迭代器遍历集合,理解Stream的懒加载和无限序列概念,查看视图在数据处理中的作用,学习线程安全的Synchronized集合,掌握并行集合的并行计算原理,以及常见的集合操作符。同时,文章提供了几个综合练习以巩固所学知识。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、迭代器

通过iterator方法从集合获得一个迭代器,通过while循环和for表达式对集合进行遍历。

object IteratorDemo {
  def main(args: Array[String]): Unit = {
    val iterator = List(1, 2, 3, 4, 5).iterator // 得到迭代器
    /*
    这里我们看看iterator 的继承关系
     def iterator: Iterator[A] = new AbstractIterator[A] {
    var these = self
    def hasNext: Boolean = !these.isEmpty
    def next(): A =
      if (hasNext) {
        val result = these.head; these = these.tail; result
      } else Iterator.empty.next()
     */
    println("--------遍历方式1 while -----------------")
    while (iterator.hasNext) {
      println(iterator.next())
    }
    println("--------遍历方式2 for -----------------")
    for(enum <- iterator) {
      println(enum) //
    }
  }
}

小结:

  • iterator 的构建实际是 AbstractIterator 的一个匿名子类,该子类提供了
    /*
    def iterator: Iterator[A] = new AbstractIterator[A] {
    var these = self
    def hasNext: Boolean = !these.isEmpty
    def next(): A =
    */
  • 该AbstractIterator 子类提供了 hasNext next 等方法
    因此,我们可以使用 while的方式,使用hasNext next 方法变量

2、流 Stream

stream是一个集合。这个集合,可以用于存放无穷多个元素,但是这无穷个元素并不会一次性生产出来,而是需要用到多大的区间,就会动态的生产末尾元素遵循lazy规则(即:要使用结果才进行计算的) 。

object StreamDemo {
  def main(args: Array[String]): Unit = {
    //创建Stream
    def numsForm(n: BigInt) : Stream[BigInt] = n #:: numsForm(n + 1)
    val stream1 = numsForm(1)
    println(stream1) // Stream(1,?)
    //取出第一个元素
    println("head=" + stream1.head) // head=1
    println(stream1.tail) // Stream(2, ?)
    println(stream1) // Stream(1, 2, ?)

    //看一个应用案例
    def multi(x:BigInt) : BigInt = {
      x * x
    }
    println(numsForm(5).map(multi)) // Stream(25, ?)

  }
}

说明:

  • Stream 集合存放的数据类型是BigInt
  • numsForm 是自定义的一个函数,函数名是程序员指定的
  • 创建的集合的第一个元素是 n , 后续元素生成的规则是 n + 1
  • 后续元素生成的规则是可以程序员指定的 ,比如 numsForm( n * 4)…

注意:如果使用流集合,就不能使用last属性,如果使用last集合就会进行无限循环

3、视图 View

Stream的懒加载特性,也可以对其他集合应用view方法来得到类似的效果

特点:
view方法产出一个总是被懒执行的集合
view不会缓存数据,每次都要重新计算,比如遍历View时

请找到1-100 中,数字倒序排列 和它本身相同的所有数。(1 2, 11, 22, 33 …)

object ViewDemo01 {
  def main(args: Array[String]): Unit = {

    //如果这个数,逆序后和原来数相等,就返回true,否则返回false
    def eq(i: Int): Boolean = {
      i.toString.equals(i.toString.reverse)
    }

    //说明: 没有使用view,常规方式
    val viewSquares1 = (1 to 100).filter(eq)
    println(viewSquares1)


    //使用view,来完成这个问题,程序中,对集合进行map,filter,reduce,fold...
    //你并不希望立即执行,而是在使用到结果才执行,则可以使用view来进行优化.
    val viewSquares2 = (1 to 100).view.filter(eq)
    println(viewSquares2)
    //遍历
    for (item <- viewSquares2) {
      println("item=" + item)
    }
  }
}
Vector(1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55, 66, 77, 88, 99)
SeqViewF(...)
item=1
item=2
item=3
item=4
item=5
item=6
item=7
item=8
item=9
item=11
item=22
。。。。
item=99

4、线程安全的集合

所有线程安全的集合都是以Synchronized开头的集合

SynchronizedBuffer
SynchronizedMap
SynchronizedPriorityQueue
SynchronizedQueue
SynchronizedSet
SynchronizedStack

5、并行集合

Scala为了充分使用多核CPU,提供了并行集合(有别于前面的串行集合),用于多核环境的并行计算。

主要用到的算法有:

  • Divide and conquer : 分治算法,Scala通过splitters(分解器),combiners(组合器)等抽象层来实现,主要原理是将计算工作分解很多任务,分发给一些处理器去完成,并将它们处理结果合并返回

  • Work stealin算法,主要用于任务调度负载均衡(load-balancing),通俗点完成自己的所有任务之后,发现其他人还有活没干完,主动(或被安排)帮他人一起干,这样达到尽早干完的目的

object ParDemo {
  def main(args: Array[String]): Unit = {

    (1 to 5).foreach(println(_))
    println()
    //这里输出的结果是无序的,说明是将println任务分配给不同cpu
    (1 to 5).par.foreach(println(_))
  }
}
object ParDemo {
  def main(args: Array[String]): Unit = {

    val result1 = (0 to 100).map{case _ => Thread.currentThread.getName}.distinct
    val result2 = (0 to 100).par.map{case _ => Thread.currentThread.getName}.distinct
    println(result1) //非并行
    println("--------------------------------------------")
    println(result2) //并行
  }
}
Vector(main)
--------------------------------------------
ParVector(ForkJoinPool-1-worker-5, ForkJoinPool-1-worker-7, ForkJoinPool-1-worker-1, ForkJoinPool-1-worker-3)

6、操作符

  • 如果想在变量名、类名等定义中使用语法关键字(保留字),可以配合反引号反引号
    val val = 42
  • 中置操作符:A 操作符 B 等同于 A.操作符(B)
  • 后置操作符:A操作符 等同于 A.操作符,如果操作符定义的时候不带()则调用时不能加括号
  • 前置操作符,+、-、!、~等操作符A等同于A.unary_操作符
  • 赋值操作符,A 操作符= B 等同于 A = A 操作符 B ,比如 A += B 等价 A = A + B
object OperatorDemo {
  def main(args: Array[String]): Unit = {

    val n1 = 1
    val n2 = 2
    val r1 = n1 + n2 // 3
    val r2 = n1.+(n2) // 3 看Int的源码即可说明
    println(r1)
    println(r2)

    val monster = new Monster
    monster + 10
    monster.+(10)

    println("monster.money=" + monster.money) // 20

    println(monster++)
    println(monster.++)
    println("monster.money=" + monster.money) // 22

    !monster
    println("monster.money=" + monster.money) // -22

  }
}

class Monster {
  var money: Int = 0

  //对操作符进行重载 (中置操作符)
  def +(n:Int): Unit = {
    this.money += n
  }
  //对操作符进行重载(后置操作符)
  def ++(): Unit = {
    this.money += 1
  }

  //对操作符进行重载(前置操作符,一元运算符)
  def unary_!(): Unit = {
    this.money = -this.money
  }
}
3
3
monster.money=20
()
()
monster.money=22
monster.money=-22

7、综合练习

练习1:

val sentence = “AAAAAAAAAABBBBBBBBCCCCCDDDDDDD”
将sentence 中各个字符,通过foldLeft存放到 一个ArrayBuffer中

object Exercise {
  def main(args: Array[String]): Unit = {
    val sentence = "AAAAAAAAAABBBBBBBBCCCCCDDDDDDD"
    val arrayBuffer = new ArrayBuffer[Char]()

    sentence.foldLeft(arrayBuffer)(putArray)
    println("arrayBuffer=" + arrayBuffer)

  }
  def putArray(arr:ArrayBuffer[Char],c:Char): ArrayBuffer[Char] = {
    //将c 放入到arr 中
    arr.append(c)
    arr
  }
}
arrayBuffer=ArrayBuffer(A, A, A, A, A, A, A, A, A, A, B, B, B, B, B, B, B, B, C, C, C, C, C, D, D, D, D, D, D, D)

练习2:

val sentence = “AAAAAAAAAABBBBBBBBCCCCCDDDDDDD”
使用映射集合,统计一句话中,各个字母出现的次数

Java:

String sentence = "AAAAAAAAAABBBBBBBBCCCCCDDDDDDD";
Map<Character, Integer> charCountMap =
	new HashMap<Character, Integer>();
char[] cs = sentence.toCharArray();
for ( char c : cs ) {
    if ( charCountMap.containsKey(c) ) {
	Integer count = charCountMap.get(c);
	charCountMap.put(c, count + 1);
    } else {
	charCountMap.put(c, 1);}}
System.out.println(charCountMap);

Scala:

object Exercise {
  def main(args: Array[String]): Unit = {
    val sentence = "AAAAAAAAAABBBBBBBBCCCCCDDDDDDD"
    val map2 = sentence.foldLeft(Map[Char,Int]())(charCount)
    println("map2=" + map2)

    //使用可变的map,效率更高.
    //1. 先创建一个可变map,作为左折叠的第一个参数
    val map3 = mutable.Map[Char,Int]()
    sentence.foldLeft(map3)(charCount2)
    println("map3=" + map3)
  }

  //使用不可变map实现
  def charCount(map:Map[Char,Int],char:Char): Map[Char,Int] = {
    map + (char -> (map.getOrElse(char,0) + 1) )
  }

  //使用可变map实现
  def charCount2(map:mutable.Map[Char,Int], char:Char): mutable.Map[Char,Int] = {
    map += (char -> (map.getOrElse(char,0) + 1) )
  }
}
map2=Map(A -> 10, B -> 8, C -> 5, D -> 7)
map3=Map(D -> 7, A -> 10, C -> 5, B -> 8)

练习3:

val lines = List(“fjw bbb hello world”, “fjw bbb aaa aaa aaa ccc ddd uuu”)
使用映射集合,list中,各个单词出现的次数,并按出现次数排序

object WorldCount {

  def main(args: Array[String]): Unit = {

    val lines = List("fjw bbb hello world", "fjw bbb aaa aaa aaa ccc ddd uuu")

    val res1 = lines.flatMap(_.split(" "))
    println("res1=" + res1)
    // res1.map 说明
    //1. 使用map,返回对偶元组 形式为
    //List((hello,1), (tom,1), (hello,1), (jerry,1), (hello,1), (jerry,1), (hello,1), (kitty,1))
    val res2 = res1.map((_, 1))
    println("res2=" + res2)
    // res2.groupBy(_._1)
    //1. 分组的根据是以元素来分组
    //2. _._1 中的第一个 _ 表示取出的各个对偶元组比如 (hello,1)
    //3. _._1 中的_1, 表示对偶元组的第一个元素,比如 hello
    //4. 因此 _._1 表示我们分组的标准是按照对偶元组的第一个元素进行分组
    //5. 返回的形式为 Map(tom -> List((tom,1)), kitty -> List((kitty,1)), jerry -> List((jerry,1), (jerry,1)), hello -> List((hello,1), (hello,1), (hello,1), (hello,1)))
    val res3 = res2.groupBy(_._1)
    println("res3=" + res3)

    // x=>(x._1, x._2.size) 传入一个匿名函数,完成统计
    //1.x 表示传入的Map中的各个元素,比如 jerry -> List((jerry,1), (jerry,1))
    //2.x._1 表示 jerry
    //3.x._2.size,表示对 List((jerry,1), (jerry,1))求size,是多少就是多少
    //4.结果是 res4=Map(han -> 2, atguigu -> 2, hello -> 1)
    //5.到此结果就出来了,但是没有排序
    val res4 = res3.map(x=>(x._1, x._2.size))
    println("res4=" + res4 )

    // res4.toList.sortBy(_._2)
    //1. toList先将map转成 list,为了下一步排序
    //5. sortBy就是排序,以对偶元组的第二个值排序,就是大小排序
    val res5 = res4.toList.sortBy(_._2)
    println("res5=" + res5)

    //如果希望从大到小排序,执行reverse即可
    val res6 = res5.reverse
    println("res6=" + res6)

  }
}
res1=List(fjw, bbb, hello, world, fjw, bbb, aaa, aaa, aaa, ccc, ddd, uuu)
res2=List((fjw,1), (bbb,1), (hello,1), (world,1), (fjw,1), (bbb,1), (aaa,1), (aaa,1), (aaa,1), (ccc,1), (ddd,1), (uuu,1))
res3=Map(world -> List((world,1)), ddd -> List((ddd,1)), ccc -> List((ccc,1)), uuu -> List((uuu,1)), bbb -> List((bbb,1), (bbb,1)), hello -> List((hello,1)), aaa -> List((aaa,1), (aaa,1), (aaa,1)), fjw -> List((fjw,1), (fjw,1)))
res4=Map(world -> 1, ddd -> 1, ccc -> 1, uuu -> 1, bbb -> 2, hello -> 1, aaa -> 3, fjw -> 2)
res5=List((world,1), (ddd,1), (ccc,1), (uuu,1), (hello,1), (bbb,2), (fjw,2), (aaa,3))
res6=List((aaa,3), (fjw,2), (bbb,2), (hello,1), (uuu,1), (ccc,1), (ddd,1), (world,1))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值