Scala集合使用flatMap、map的优化及迭代器的原理

本文探讨了Scala中flatMap和map在处理集合数据时的作用,以及如何将列表转换为映射。同时,解释了迭代器模式的原因和工作原理,强调了其在多线程环境下的优势。通过源码分析,展示了flatMap和map操作的内部实现,并指出在使用迭代器时如何避免内存占用过大的问题。最后,讨论了何时开始执行迭代器的方法,并提供了逻辑架构图以帮助理解。
摘要由CSDN通过智能技术生成

1. flatMap、map

集合使用flatMap和map来处理数据

	val listStr01 = List("hello word", "hello hdfs", "hadoop hdfs")
    //原版写法、flatMap扁平化
//    val listStr02 = listStr01.flatMap((x:String) =>x.split(" "))
    val listStr02 = listStr01.flatMap(_.split(" "))//简化版匿名函数 、按空格切割、将listStr01中三个元素切分成六个并放入新的list中
    listStr02.foreach(println)
// 如何将list转换为map
    println("---------------将list转换成map--------------")
    val listToMap = listStr02.map((_, 1))//map就是映射  _就是数据,以数据当key 1就是固定的value
    listToMap.foreach(println)

缺点:内存扩大了n倍、每一步计算内存都保留对象数据、 什么技术可以解决数据计算中间状态占用内存这一问题? iterator迭代器

2. 什么是迭代器、为什么会有迭代器模式

在集合中、因为迭代器迭代时是通过指针进行迭代的、如果多线程同时迭代一个集合的话 第一个迭代器迭代到指针3的时候 第二个迭代器来了、它迭代的话是从指针3开始迭代的、并不是从头开始迭代、它存放的只有指针

优化代码:

 val it = listStr01.iterator //什么是迭代器、为什么会有迭代器模式、  迭代器里不存数据
    // 集合中  因为迭代器迭代时是通过指针进行迭代的、如果多线程同时迭代一个集合的话 第一个迭代器迭代到指针3的时候 第二个迭代器来了、它迭代的话是从指针3开始迭代的、并不是从指针1开始迭代
    val itStr02 = it.flatMap(_.split(" "))//简化版匿名函数 、按空格切割、将listStr01中三个元素切分成六个并放入新的list中
//    itStr02.foreach(println)
    // 如何将list转换为map
    val itStr02tToMap = itStr02.map((_, 1))//map就是映射  _就是数据,以数据当key 1就是固定的value
    itStr02tToMap.foreach(println) //这里不出现数据是因为上面 itStr02 遍历的时候将迭代器的指针跌倒到最后了、所以将上面foreach注释了就可以了

2.1 首先我们来看迭代器的源码

在这里插入图片描述

2.2 当咱们数据集进行迭代时、它首先会去new 一个抽象的迭代器、其中包含hashNext 和 next 方法,

//按照我的理解 StrictOptimizedLinearSeqOps.this 代表它自己、例:当前的那个数据集合
private[this] var current = StrictOptimizedLinearSeqOps.this
// 取反、如果当前无数据 会返回false
def hasNext = !current.isEmpty
// 它是将集合的首位、头部赋值给了r、然后将集合头部往后的数据重新赋给了current、返回一个r 如果是while循环的话 它会一直往后取、直到current.tail赋给current 为空的时候,走到def hasNext = !current.isEmpty 返回一个false 然后结束
def next() = { val r = current.head; current = current.tail; r }

2.3 接着咱们使用迭代器调用flatMap方法的时候 val itStr02 = it.flatMap(_.split(" "))

在这里插入图片描述
它还是会先new 一个抽象的迭代器、默认给cur置空、_hasNext默认设置为 -1 ,接着咱们看它flatMap的hasNext、和next方法
在这里插入图片描述

    def hasNext: Boolean = {
    //因为默认值给了-1
      if (_hasNext == -1) {
      //cur 取反 为true 
        while (!cur.hasNext) {
        //self代表自己、意思说当前方法的迭代器、它问父迭代器有没有数据、咱们是通过it.flatMap(_.split(" "))调用的,如果父迭代器告诉他有数据那就取反为false不进行if后的操作
          if (!self.hasNext) {
            _hasNext = 0
            // since we know we are exhausted, we can release cur for gc, and as well replace with
            // static Iterator.empty which will support efficient subsequent `hasNext`/`next` calls
            cur = Iterator.empty
            return false
          }
          //父迭代器有数据将会调用这个方法
          nextCur()
        }
        _hasNext = 1
        true
      } else _hasNext == 1
    }

flatMap的nextCur方法:

    private[this] def nextCur(): Unit = {
      cur = null
      //令cur 进行赋值、意思是从父迭代器中拿出了一条元素(hello word),传给我们的方法就是前面定义的split、然后将数组转换成迭代器付给了cur 
      cur = f(self.next()).iterator
      _hasNext = -1
    }

flatMap的next 方法

    def next(): B = {
    //这里的话它是看hasNext这个方法有没有值有的话不执行
      if (hasNext) {
        _hasNext = -1
      }
      //它会得到hasNaxt中split切割的数据、按照数据集来看就是 hello
      cur.next()
    }

这里最重要的就是cur,只要下游掉了hasNext 方法 cur为空 hasNaxt方法就会偷摸填充cur、下游只要掉next方法、next只会从cur中取元素返回给下游

2.3 咱们看 map方法、它也是new 一个抽象迭代器,它就负责掉父亲的hasNext和next方法和你的匿名函数

在这里插入图片描述

  def map[B](f: A => B): Iterator[B] = new AbstractIterator[B] {
    override def knownSize = self.knownSize
    //调用父亲的hasNext
    def hasNext = self.hasNext
    //调用父亲的next
    def next() = f(self.next())
  }

2.4 什么时候开始执行这些迭代器呢?

//当咱们使用while循环用迭代器调用hasNext和next的时候方法才会执行
    while (itStr02tToMap.hasNext) {
      val tuple = itStr02tToMap.next()
      println(tuple)
    }

逻辑架构图:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Angzush

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值