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


    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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    缺点:内存扩大了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注释了就可以了
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    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 }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    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
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    flatMap的nextCur方法:

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

    flatMap的next 方法

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

    这里最重要的就是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())
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

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

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

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

  • 相关阅读:
    二十二、环境变量和模式
    Kafka 控制器(controller)
    Kotlin & Compose Multiplatform 跨平台开发实践之加入 iOS 支持
    【linux命令讲解大全】014.Git:分布式版本控制系统的先驱和常用命令清单(三)
    mysql日期月份相关函数
    Mockito单元测试
    Java 数组、日期和时间
    [操作系统笔记]连续分配管理方式
    c++中的标准模板库
    【软件设计师-从小白到大牛】上午题基础篇:第七章 程序设计语言与语言处理程序基础
  • 原文地址:https://blog.csdn.net/Angzush/article/details/126645561