java 高阶函数_常用高阶函数:汇总

今天我们一起看看几个起到汇总作用的高阶函数。

count()

数组、集合、区间和序列都扩展了 count() 函数,它有两个重载形式,一是无参的形式,它返回总数,一般直接通过调用 size 属性实现;二是接受一个 Boolean 函数作为参数的形式,它 返回符合条件的元素数目。Array.count((T) -> Boolean) 的实现如下:

inline fun Array.count(predicate: (T) -> Boolean): Int {

var count = 0

for (element in this) if (predicate(element)) count++

return count

}

与我们在 Java 中手工写的循环完全一致。这个函数在使用时也很简单:

fun Int.countOfBinaryOnes() =

(0..31).count { (this shr it) and 1 == 1 }

我们这里写一个小函数,它扩展了 Int 类型,返回这个数字二进制中 1 的数量。如果用 Java 写,需要这样:

int count = 0;

for (int i = 0; i < 32; i++)

if (((num >> i) & 1) == 1)

count++;

return count;

从处理函数的长度来说,Java 因为支持位运算符,比 Kotlin 用中缀函数的形式要短一些。不过,Java 需要定义一个临时变量 count ,也没有封装循环,比 Kotlin 函数式编程更长也更容易出错。

joinTo() 与 joinToString()

joinToString() 函数是对 joinTo() 函数的封装,可以方便地联结字符串,我们看一下 Array.joinTo() 函数的参数列表和实现:

fun Array.joinTo(

buffer: A,

separator: CharSequence = ", ",

prefix: CharSequence = "",

postfix: CharSequence = "",

limit: Int = -1,

truncated: CharSequence = "...",

transform: ((T) -> CharSequence)? = null

): A {

buffer.append(prefix)

var count = 0

for (element in this) {

if (++count > 1) buffer.append(separator)

if (limit < 0 || count <= limit) {

buffer.appendElement(element, transform)

} else break

}

if (limit >= 0 && count > limit) buffer.append(truncated)

buffer.append(postfix)

return buffer

}

它总共有 7 个参数,除了第一个 Appendable 类型的 buffer 参数外,后面 6 个参数都有默认值。我们依次看看它们怎么用:buffer:Appendable 类型,写入数据的目标。java.lang.Appendable 接口表示“可追加字符序列”的对象,常用的 StringBuilder、StringBuffer、Writer、CharBuffer 都实现了这个接口。这里用到的 Appendable.appendElement(T, ((T) -> CharSequence)?) 是 Kotlin 为 Appendable 接口扩展的高阶函数,会根据 tranform 函数处理 element 并追加到 Appendable 里。

separator:CharSequence 类型,默认为 ", ",分隔各元素的字符串。

prefix、postfix:CharSequence 类型,默认为空字符串,目标的前缀和后缀。

limit:Int 类型,默认为 -1,联结元素限制数。

根据源码发现:当 limit < 0 或 limit > 元素数时,联结所有元素,不添加删节符号;当 limit = 0 时,联结所有元素,添加删节符号;当 0 < limit < 元素数时,联结前 limit 个元素,并在最后添加删节符号。

truncated:CharSequence 类型,默认为 "...",删节符号,与 limit 配合使用。

transform:((T) -> CharSequence)? 类型,默认为 null,处理元素的函数。每个元素会经这个函数处理后添加到 buffer 内。

比起 joinTo(),joinToString() 更加常用,它是对 joinTo() 的封装:

fun Array.joinToString(separator: CharSequence = ", ", prefix: CharSequence = "", postfix: CharSequence = "", limit: Int = -1, truncated: CharSequence = "...", transform: ((T) -> CharSequence)? = null): String {

return joinTo(StringBuilder(), separator, prefix, postfix, limit, truncated, transform).toString()

}

比如,可以这样列出文件夹内的所有文件:

fun File.listFilesReadable(): String {

return if (isDirectory)

listFiles().joinToString("\n") { it.name }

else name

}

如果被扩展的 File 对象是文件夹,就调用 listFiles() 方法列出文件夹内的所有文件,再用 \n 字符将文件名联结起来;如果是文件则返回文件名。

sumBy() 和 sumByDouble()

这两个函数很简单,都接受一个函数,将元素转换为数字再加和。我们看一下 Array.sumBy() 函数的实现:

inline fun Array.sumBy(selector: (T) -> Int): Int {

var sum: Int = 0

for (element in this) {

sum += selector(element)

}

return sum

}

sumByDouble() 则是把 sum 变量定义为 Double 类型,实现方式是一样的。

此外,要进行简单的汇总,只需要调用 sum() 函数即可。

最后来个复杂一丁点的例子吧:给 File 类型扩展一个函数 fullSize(),返回这个 File 的总体积。

这个问题需要处理是否为 文件夹 的情况,要遍历文件夹内文件并返回体积之和,我的实现如下:

fun File.fullSize(): Long {

return if (isDirectory)

listFiles().map { it.fullSize() }.sum()

else length()

}

这里用迭代处理文件夹,如果是文件夹则将子文件的 fullSize() 映射到一个 List 里,然后用 sum() 求和。可以思考一下,我们为什么不用 sumBy() 函数呢?因为 sumBy() 函数返回 Int 类型,而 File.length() 返回 Long 类型,可能发生溢出,我们这里用的 map() 和 sum() 都提供了 Long 类型返回值的重载,没有溢出的危险。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值