kotlin 添加第一个 集合_kotlin 集合的操作

kotlin 集合的操作

1.集合式函数操作

a. filter和map

filter即过滤,它会遍历集合并选出应用给定lambda后返回未true的元素。使用它可以移除不满足条件的元素(数据源并不会改变)

例如:

val list = listOf(1,2,3,4,5,6)

//过滤奇数,保留偶数

println(list.filter { it % 2==0 }) //[2, 4, 6]

//数据源

val people = listOf(Person("jack", 29),

Person("nick", 23),

Person("jone", 26))

//过滤掉年龄小于25的,保留年龄大于25的

println(people.filter { it.age>25 })

//[Person(name=jack, age=29), Person(name=jone, age=26)]

map对集合每一个元素应用给定的函数并把结果收集到一个新集合,即元素变换

val list = listOf(1,2,3,4,5,6)

//map把元素换为它的平方集合

println(list.map { it*it }) //[1, 4, 9, 16, 25, 36]

//使用map将元素为Person类型转换为String

println(people.map { it.name }) //[jack, nick, jone]

//可以使用成员引用简写

println(people.map(Person::name)) //[jack, nick, jone]

b. all,any,count,find:对集合的判断应用

检查集合中所有的元素是否都符合某个条件(又或是是否存在符合的元素)。它们是通过all和any函数表达。count为检查有多少个元素满足判断式,find函数返回第一个符合条件的元素。

//数据源

val people = listOf(Person("jack", 29),

Person("nick", 23),

Person("jone", 26))

//年龄是否满足23

val isAge23={p:Person->p.age<=23}

//检查集合看是否所有元素满足(all)

println(people.all(isAge23)) //false

//检查集合中是否至少存在一个匹配的元素(any)

println(people.any(isAge23))//true

//检查有多少个元素满足判断式(count)

println(people.count(isAge23))//1

//找到第一个满足判断式的元素(find)

//如有多个匹配返回其中第一个元素,没有返回null。同义函数firstOrNull。

println(people.find(isAge23)) //Person(name=nick, age=23)

值得注意的是!all(不是所有)加上某个条件,应该用any取反

val list = listOf(1,2,3)

println(!list.all{it==3})//此种方式不推荐

println(list.any { it!=3 })//推荐此种方式定义(lambda参数中条件取反)

再就是count和.size。count方法只是跟踪匹配元素的数量,不关心元素本身,所以更高效。.size需要配合filter过滤从而中间会创建新集合用来存储。

//.size的方式(具体使用情况看实际,只关心数量不推荐此方式)

println(people.filter (isAge23).size)

c. groupBy:把列表转换成分组的map

如果需要把不同的元素划分到不同的分组,使用groupaBy事半功倍。

//数据源

val people = listOf(Person("jack", 29),

Person("nick", 23),

Person("jone", 23))

//使用group按年龄分组,返回结果是map

println(people.groupBy{it.age})

// {29=[Person(name=jack, age=29)], 23=[Person(name=nick, age=23), Person(name=jone, age=23)]}

每一个分组都存储在一个列表中,这里结果类型实质是Map>,mapKeys或mapValues函数也能作用于它。

val list = listOf("a", "ab", "abc", "b")

//值得注意的是,这里的first不是String的成员,而是一个扩展(可成员引用)

println(list.groupBy(String::first))//{a=[a, ab, abc], b=[b]}

2.kotlin列表自有方法的链式调用问题

例如:查找列表中年龄最大的人

val people = listOf(PersonB("AAA",23),PersonB("BBB",18),PersonB("CCC",26),PersonB("DDD",5))

println(people.filter { println("filter:${it.name}"); it.age == people.maxBy { println("maxBy::${it.name}");it.age }!!.age })

运行结果为:

filter:AAA

maxBy::AAA

maxBy::BBB

maxBy::CCC

maxBy::DDD

filter:BBB

maxBy::AAA

maxBy::BBB

maxBy::CCC

maxBy::DDD

filter:CCC

maxBy::AAA

maxBy::BBB

maxBy::CCC

maxBy::DDD

filter:DDD

maxBy::AAA

maxBy::BBB

maxBy::CCC

maxBy::DDD

[PersonB(name=CCC, age=26)]

每次filter都走了一遍maxBy方法

推荐使用以下用法

val maxAge = people.maxBy { println("maxBy::${it.name},");it.age }!!.age

println(people.filter { println("filter:${it.name}"); it.age == maxAge })

运行结果为:

maxBy::AAA,

maxBy::BBB,

maxBy::CCC,

maxBy::DDD,

filter:AAA

filter:BBB

filter:CCC

filter:DDD

[PersonB(name=CCC, age=26)]

3.惰性集合操作:序列

如果对java8的lambda熟悉,一定会知道stream流的存在。上面大部分的lambda函数会及早的创建中间集合(每一步中间结果都被存储在一个临时列表)。因此如果数据过多的话链式调用就会特别低效。而序列恰好能避免创建这些临时中间对象,从而解决这一问题。

people.asSequence() //把初始集合转成序列

.map (Person::name) //序列支持和集合一样的api

.filter { it.startsWith("j") }

.toList() //把结果序列转换未序列表

惰性集合操作入口就是Sequence接口,这个接口就是表示可以逐个列举的元素序列。Sequence只提供一个方法,iterator(用来从序列里获取值)。

Sequence接口强大在于操作实现的形式。序列中元素求值是惰性的。值得注意的是asSequence()是扩展函数。

执行序列操作:中间和末端操作

序列操作分为两类:中间的和末端。一次中间操作返回值是另一个序列(知道如何变换原始序列中的元素)。而一次末端操作返回的是一个结果,这个结果可能是集合,元素,数字或者其它从初始集合的变换序列中获取的任意对象。

people.asSequence()

.map (Person::name) //中间操作

.filter { it.startsWith("j") } //中间操作

.toList() //末端操作

中间操作始终都是惰性的,不会输出任何结果。

//不会输出任何内容,lambdaa变换被延期,只有获取结果时才会被调用(末端操作)

listOf(1,2,3,4).asSequence()

.map { print("map($it) ");it*it }

.filter { print("filter($it)");it%2==0 }

加上末端操作才会进行真正的结果输出

listOf(1,2,3,4).asSequence()

.map { print("map($it) ");it*it }

.filter { print("filter($it)");it%2==0 }

.toList() // 末端操作触发执行所有延期计算

//map(1) filter(1)map(2) filter(4)map(3) filter(9)map(4) filter(16)

着重注意的是计算顺序,序列的操作是按顺序应用在每一个元素上:处理第一个元素后,再完成第二个元素,以此类推。

这也意味着有部分元素根本不会发生任何变换,举个map和find的例子。先把一个数字映射成它的平方,然后找到第一个比3大的条目。

println(listOf(1,2,3,4).asSequence().map { it*it }.find { it>3 })

如果同样的操作被应用在集合上,那么map结果会先被求出来,然后会把中间集合中满足的判断式的元素找出来。而对于序列来说,惰性方法意味着可以跳过处理部分元素。这也是及早求值(用集合)和惰性求值(用序列)的区别。

集合上执行操作的顺序是会影响性能的。再举个例子,用不同的操作顺序找出上述数据源person集合中长度小于某个限制的人名。

/数据源

val people = listOf(Person("jk", 29),

Person("nec", 23),

Person("jojo", 23))

//先map再filter

println(people.asSequence().map (Person::name ).filter { it.length>2 }.toList())//[nec, jojo]

//先filter再map

println(people.asSequence().filter { it.name.length>2 }.map(Person::name).toList())//[nec, jojo]

结果当然是一样的,不同的是如果map在前,那么是每个元素都进行变换后在去过滤,而filter在前,则是先过滤在变换(被过滤掉的不会进行变换)。

使用序列的效率问题

执行如下代码不适用序列

fun computeRunTime(action: (() -> Unit)?) {

val startTime = System.currentTimeMillis()

action?.invoke()

println("the code run time is ${System.currentTimeMillis() - startTime} ms")

}

fun main(args: Array) = computeRunTime{

(0..10000000)

.map { it + 1 }

.filter { it % 2 == 0 }

.count { it < 100 }

.run {

println("by using sequence result is $this")

}

}

//by using sequence result is 49

//the code run time is 2755 ms

以下为使用序列

fun computeRunTime(action: (() -> Unit)?) {

val startTime = System.currentTimeMillis()

action?.invoke()

println("the code run time is ${System.currentTimeMillis() - startTime} ms")

}

fun main(args: Array) = computeRunTime{

(0..10000000)

.asSequence()

.map { it + 1 }

.filter { it % 2 == 0 }

.count { it < 100 }

.run {

println("by using sequence result is $this")

}

}

//by using sequence result is 49

//the code run time is 150 ms

当运算级降低时

不适用序列

fun computeRunTime(action: (() -> Unit)?) {

val startTime = System.currentTimeMillis()

action?.invoke()

println("the code run time is ${System.currentTimeMillis() - startTime} ms")

}

fun main(args: Array) = computeRunTime{

(0..1000)

.map { it + 1 }

.filter { it % 2 == 0 }

.count { it < 100 }

.run {

println("by using sequence result is $this")

}

}

//by using sequence result is 49

//the code run time is 21 ms

使用序列

fun computeRunTime(action: (() -> Unit)?) {

val startTime = System.currentTimeMillis()

action?.invoke()

println("the code run time is ${System.currentTimeMillis() - startTime} ms")

}

fun main(args: Array) = computeRunTime{

(0..1000)

.asSequence()

.map { it + 1 }

.filter { it % 2 == 0 }

.count { it < 100 }

.run {

println("by using sequence result is $this")

}

}

//by using sequence result is 49

//the code run time is 31 ms

由此可以看出序列比较适合运算级数比较大的场景

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值