Kotlin中的Iterator与护士小姐姐

Screen Shot 2020-12-12 at 11.53.12 PM.png

隐喻:护士小姐姐和医生

分诊

Iterator如同一个护士小姐姐。
护士小姐姐在诊室外组织病人们排队。
病人们怎么排序?
挂号的顺序、递交病历本的顺序还是颜值,
护士小姐姐说了算。

Iterator的调用者如同医生。
医生坐在诊室里面,专心致志的给她面前的病人看病。
看完之后只需要喊一声:“下一个”。

接下来我们一起看看Iterator是怎么做的。

从一个优雅的例子开始

Kotlin官方文档中有个例子Iterators,简洁优雅。

class Animal(val name: String)

class Zoo(val animals: List<Animal>) {

    operator fun iterator(): Iterator<Animal> {             // 1
        return animals.iterator()                           // 2
    }
}

fun main() {

    val zoo = Zoo(listOf(Animal("zebra"), Animal("lion")))

    for (animal in zoo) {                                   // 3
        println("Watch out, it's a ${animal.name}")
    }

}

透过它我们来看看Iterator的优点。
它将数据结构封装在类的内部,调用者无需关心数据是如何存储的。
并且有迭代器的类可以使用for in循环访问,这使得代码非常接近自然语言,易读而便于维护。

简约的文档并不能使我感到满足

官网中例子对应的英文文档相当的简约,即便如此,所谓的kotlin中文网也没有对应的译文。我只好手动翻译:

迭代器
在你的类中,你能通过实现iterator操作符来定义你自己的迭代器。
...此处省略刚才举过的例子代码十几行...
1. 在类中定义一个迭代器。它必须命名为iterator,并且被关键字operator修饰。
2. 返回的迭代器,需包括下列成员函数
· next(): Animal
· hasNext(): Boolean
3. 使用自定义迭代器循环遍历动物园中的动物
迭代器可以用扩展函数的形式声明。

例子中的迭代器相当简单,仅仅是把自己的list成员的iterator透传出来。如果有特殊需求怎么办呢?

经过摸索发现我们需要定义自己的Iterator类。

大体思路:

如果当前数据中没有合适的iterator,就自己定义一个内部类xxIterator,
这个内部类,可以访问到外部类的所有数据,
这个内部类,可以将遍历外部数据相关的逻辑和遍历状态封装起来。
这个内部类,符合Iterator接口规范(实现next和hasNext函数)
最后将这个内部类通过外部类的iterator接口提供给外面用。

关键代码如下:

class Hospital(val femalePatients: List<FemalePatient>, val malePatients: List<MalePatient>) {

    inner class PatientIterator : Iterator<Patient> {
        //...
        public override operator fun next(): Patient {
            //...
        }

        public override operator fun hasNext(): Boolean {
            //...
        }
    }

    operator fun iterator(): Iterator<Patient> = PatientIterator()
}

需要注意的细节:

  1. Kotlin中内嵌类需要 inner修饰
  2. 重载成员需要用override修饰
  3. next、hasNext和iterator 需要用operator修饰

完整代码:

package top.ovo.hospital

// 基类病人 
open class Patient(val name: String) {
    open fun name(): String {
        return name
    }
}

// 男病人
class MalePatient(name: String) : Patient(name) {
    override fun name(): String {
        return "$name\t♂"
    }
}

// 女病人
class FemalePatient(name: String) : Patient(name) {
    override fun name(): String {
        return "$name\t♀"
    }
}

// 医院
class Hospital(val femalePatients: List<FemalePatient>, val malePatients: List<MalePatient>) {

    inner class PatientIterator : Iterator<Patient> {
        private var femalePatientsIndex: Int = 0
        private var malePatientsIndex: Int = 0

        public override operator fun next(): Patient {

            if (femalePatientsIndex <femalePatients.size) {
                return femalePatients.get(femalePatientsIndex++)
            }
            return malePatients.get(malePatientsIndex++)
        }

        public override operator fun hasNext(): Boolean {

            return femalePatientsIndex < femalePatients.size ||
                malePatientsIndex < malePatients.size
        }
    }

    operator fun iterator(): Iterator<Patient> = PatientIterator()
}

fun main() {

    println("--- 这里是分诊台 ---")

    val hospital = Hospital(
        listOf(
            FemalePatient("赵丽影"),
            FemalePatient("关晓童"),
            FemalePatient(" 杨蜜 "),
            FemalePatient(" 柳颜 ")
        ),
        listOf(
            MalePatient(" 马匀 "),
            MalePatient("张一明"),
            MalePatient("马化疼"),
            MalePatient("李彦红")
        )
    )

    for (Patient in hospital) {
        println("${Patient.name()}\t正在排队")
    }
}


代码说明:

首先我们声明一个基类“Patient”有共同的属性“name”。

然后按照性别派生了MalePatient和FemalePatient,name()方法中提供了个性化的返回。

“Hospital”类中包含两个队列,malePatients和femalePatients

内嵌类“PatientIterator”中封装了对两个病人列表的遍历逻辑:

先遍历女病人列表,然后遍历男病人列表。

如果都遍历完则通过hasNext返回没有病人了

“Hospital”中iterator()函数创建并返回了PatientIterator的实例

最后的main函数中我们使用forIn语句循环hospital实例得到队列中的每个病人。

运行结果:

$  ~/.sdkman/candidates/kotlin/current/bin/kotlinc hospital.kt -include-runtime -d hospital.jar && java -jar hospital.jar
--- 这里是分诊台 ---
赵丽影  ♀       正在排队
关晓童  ♀       正在排队
 杨蜜   ♀       正在排队
 柳颜   ♀       正在排队
 马匀   ♂       正在排队
张一明  ♂       正在排队
马化疼  ♂       正在排队
李彦红  ♂       正在排队
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容

  • Kotlin 知识梳理系列文章 Kotlin 知识梳理(1) - Kotlin 基础Kotlin 知识梳理(2) ...
    泽毛阅读 2,082评论 1 3
  • 前言 人生苦多,快来 Kotlin ,快速学习Kotlin! 什么是Kotlin? Kotlin 是种静态类型编程...
    任半生嚣狂阅读 26,134评论 9 118
  • 到本章为止,kotlin基本的知识点都记录完毕。还有关于一些泛型和反射的知识点后续会更新上来,知识点和内容来自《K...
    Haife阅读 3,426评论 0 6
  • 五、Lambda编程 1.Lambda表达式和成员引用 Lambda简介:作为函数参数的代码块。可以理解为简化表达...
    TomyZhang阅读 442评论 0 0
  • 参考文档 https://kotlinlang.org/ Kotlin是一种在Java虚拟机上运行的静态类型编程语...
    JunChow520阅读 471评论 0 1