Java8-Stream(Kotlin)
一、Stream基本用法
-
java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。
-
这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
-
元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。
我们对比kotlin的高阶拓展函数实现:
实体类
data class Apple(var id: Int?, var name: String?, var money: BigDecimal?, var num: Int?) {
constructor() : this(null, null, null, null)
fun getMe() = this
fun getMoney() = money!!.toDouble()
}
存进List
val appleList = ArrayList<Apple>() //存放apple对象集合
val apple1 = Apple(1, "苹果1", BigDecimal("3.25"), 10)
val apple12 = Apple(1, "苹果2", BigDecimal("1.35"), 20)
val apple2 = Apple(2, "香蕉", BigDecimal("2.89"), 30)
val apple3 = Apple(3, "荔枝", BigDecimal("9.99"), 40)
appleList.add(apple1)
appleList.add(apple12)
appleList.add(apple2)
appleList.add(apple3)
分组
List里面的对象元素,以某个属性来分组,例如,以id分组,将id相同的放在一起:
appleList.also { println("Java Stream:") }.stream().collect(Collectors.groupingBy(Apple::id)).forEach { (t, u) ->
println("\t\t$t -> $u")
}
appleList.also { println("Kotlin Stream:") }.groupBy(Apple::id, Apple::getMe).forEach { (t, u) ->
println("\t\t$t -> $u")
}
List转Map
List转Map id为key,apple对象为value,可以这么做:
//Java
appleList.also { println("Java Stream:") }.stream().collect(Collectors.toMap(Apple::id, fun(a: Apple) = a) { it1, it2 ->
if (it1.name == "苹果2") it1 else it2
}).forEach { (t, u) ->
println("\t\t$t -> $u")
}
//Kotlin
appleList.also { println("Kotlin Stream:") }.associateBy { it.id }.forEach { (t, u) ->
println("\t\t$t -> $u")
}
过滤Filter
从集合中过滤出来符合条件的元素:
//java
appleList.also { println("Java Stream:") }.stream().filter { it.name != "香蕉" }.collect(Collectors.toList()).forEach { println("\t\t$it") }
//kotlin
appleList.also { println("Kotlin Stream:") }.filter { it.name != "香蕉" }.forEach { println("\t\t$it") }
求和
将集合中的数据按照某个属性求和:
//java
println("\t\t总金额为:${appleList.also { println("Java Stream:") }.stream().collect(Collectors.summingDouble(Apple::getMoney))}元")
//kotlin
println("\t\t总金额为:${appleList.also { println("Kotlin Stream:") }.sumByDouble { it.getMoney() }}元")
最大值&最小值
求集合中的数据某个属性的最大值&最小值:
//java
println("最大值:" + appleList.stream().max { o1, o2 -> if (o1.num!! > o2.num!!) 1 else -1 }.get())
println("最小值:" + appleList.stream().min { o1, o2 -> if (o1.num!! > o2.num!!) 1 else -1 }.get())
//kotlin
println("最大值:" + appleList.maxWith(kotlin.Comparator { o1, o2 -> if (o1.num!! > o2.num!!) 1 else -1 }))
println("最小值:" + appleList.minWith(kotlin.Comparator { o1, o2 -> if (o1.num!! > o2.num!!) 1 else -1 }))
去重
去除对于相同id的对象:
appleList.also { println("去重") }.distinctBy { it.id }.forEach { println("\t\t$it") }
完整代码
data class Apple(var id: Int?, var name: String?, var money: BigDecimal?, var num: Int?) {
constructor() : this(null, null, null, null)
fun getMe() = this
fun getMoney() = money!!.toDouble()
}
fun main(args: Array<String>) {
val appleList = ArrayList<Apple>() //存放apple对象集合
val apple1 = Apple(1, "苹果1", BigDecimal("3.25"), 10)
val apple12 = Apple(1, "苹果2", BigDecimal("1.35"), 20)
val apple2 = Apple(2, "香蕉", BigDecimal("2.89"), 30)
val apple3 = Apple(3, "荔枝", BigDecimal("9.99"), 40)
appleList.add(apple1)
appleList.add(apple12)
appleList.add(apple2)
appleList.add(apple3)
//1、分组 List里面的对象元素,以某个属性来分组,例如,以id分组,将id相同的放在一起:
appleList.also { println("Java Stream:") }.stream().collect(Collectors.groupingBy(Apple::id)).forEach { (t, u) ->
println("\t\t$t -> $u")
}
appleList.also { println("Kotlin Stream:") }.groupBy(Apple::id, Apple::getMe).forEach { (t, u) ->
println("\t\t$t -> $u")
}
println()
// 2、List转Map id为key,apple对象为value,可以这么做:
appleList.also { println("Java Stream:") }.stream().collect(Collectors.toMap(Apple::id, fun(a: Apple) = a) { it1, it2 ->
if (it1.name == "苹果2") it1 else it2
}).forEach { (t, u) ->
println("\t\t$t -> $u")
}
appleList.also { println("Kotlin Stream:") }.associateBy { it.id }.forEach { (t, u) ->
println("\t\t$t -> $u")
}
println()
//3、过滤Filter 从集合中过滤出来符合条件的元素:
appleList.also { println("Java Stream:") }.stream().filter { it.name != "香蕉" }.collect(Collectors.toList()).forEach { println("\t\t$it") }
appleList.also { println("Kotlin Stream:") }.filter { it.name != "香蕉" }.forEach { println("\t\t$it") }
println()
//4.将集合中的数据按照某个属性求和:
println("\t\t总金额为:${appleList.also { println("Java Stream:") }.stream().collect(Collectors.summingDouble(Apple::getMoney))}元")
println("\t\t总金额为:${appleList.also { println("Kotlin Stream:") }.sumByDouble { it.getMoney() }}元")
println()
//5.求集合中的数据某个属性的最大值&最小值
println("最大值:" + appleList.stream().max { o1, o2 -> if (o1.num!! > o2.num!!) 1 else -1 }.get())
println("最小值:" + appleList.stream().min { o1, o2 -> if (o1.num!! > o2.num!!) 1 else -1 }.get())
println("最大值:" + appleList.maxWith(kotlin.Comparator { o1, o2 -> if (o1.num!! > o2.num!!) 1 else -1 }))
println("最小值:" + appleList.minWith(kotlin.Comparator { o1, o2 -> if (o1.num!! > o2.num!!) 1 else -1 }))
}
输出
Java Stream:
1 -> [Apple(id=1, name=苹果1, money=3.25, num=10), Apple(id=1, name=苹果2, money=1.35, num=20)]
2 -> [Apple(id=2, name=香蕉, money=2.89, num=30)]
3 -> [Apple(id=3, name=荔枝, money=9.99, num=40)]
Kotlin Stream:
1 -> [Apple(id=1, name=苹果1, money=3.25, num=10), Apple(id=1, name=苹果2, money=1.35, num=20)]
2 -> [Apple(id=2, name=香蕉, money=2.89, num=30)]
3 -> [Apple(id=3, name=荔枝, money=9.99, num=40)]
Java Stream:
1 -> Apple(id=1, name=苹果2, money=1.35, num=20)
2 -> Apple(id=2, name=香蕉, money=2.89, num=30)
3 -> Apple(id=3, name=荔枝, money=9.99, num=40)
Kotlin Stream:
1 -> Apple(id=1, name=苹果2, money=1.35, num=20)
2 -> Apple(id=2, name=香蕉, money=2.89, num=30)
3 -> Apple(id=3, name=荔枝, money=9.99, num=40)
Java Stream:
Apple(id=1, name=苹果1, money=3.25, num=10)
Apple(id=1, name=苹果2, money=1.35, num=20)
Apple(id=3, name=荔枝, money=9.99, num=40)
Kotlin Stream:
Apple(id=1, name=苹果1, money=3.25, num=10)
Apple(id=1, name=苹果2, money=1.35, num=20)
Apple(id=3, name=荔枝, money=9.99, num=40)
Java Stream:
总金额为:17.48元
Kotlin Stream:
总金额为:17.48元
最大值:Apple(id=3, name=荔枝, money=9.99, num=40)
最小值:Apple(id=1, name=苹果1, money=3.25, num=10)
最大值:Apple(id=3, name=荔枝, money=9.99, num=40)
最小值:Apple(id=1, name=苹果1, money=3.25, num=10)
数据来自:https://zhuanlan.zhihu.com/p/197291348
二、Stream高级用法
flatMap
返回一个流,包含将此流中的每个元素替换为通过给定函数映射应用于每个元素而生成的映射流的内容
问:使用stream按照链式编程将map的k v放进一个list并返回。
hashMapOf("a" to "b", "c" to "d")
.entries
.stream()
.flatMap {
Arrays.stream(arrayOf(it.key, it.value))
}
.toList()
.forEach(::println)
println()
hashMapOf("a" to "b", "c" to "d")
.entries
.stream()
.flatMap(object : Function<Map.Entry<String, String>, Stream<String>> {
override fun apply(t: Map.Entry<String, String>): Stream<String> {
return Stream.of(t.key, t.value)
}
})
.forEach {
println(it)
}
//输出
//a
//b
//c
//d
参考:
[]: https://www.cnblogs.com/yucy/p/10260014.html “flatmap与map”
reduce
方法需要一个函数式接口参数,该函数式接口需要两个参数,返回一个结果(reduce中返回的结果会作为下次累加器计算的第一个参数),也就是累加器
问:将list的元素合并成一个字符串并返回
listOf("a", "b", "c", "d")
.stream()
.reduce { t: String?, u: String? -> //t参数是
//① return@reduce t + u
//② t + u
t + u
}.get()
//输出:abcd
问:将list的元素合并成一个字符串后再头部拼接指定字符串并返回
val reduce = listOf("a", "b", "c", "d")
.stream()
.reduce("我草") { t: String?, u: String? ->
//① return@reduce t + u
//② t + u
t + u
}
//输出:我草abcd
三参用法:提供一个不同于Stream
中数据类型的初始值,通过累加器规则迭代计算Stream
中的数据,最终得到一个同初始值同类型的结果
val list = arrayListOf("a", "b", "c", "d")
list
.stream()
.reduce(ArrayList<String>(), { u1, u2 ->
println("第一个参数")
u1.add("user-$u2")
u1
}, { t1, t2 ->
println("第二个参数")
null
})
?.forEach {
println(it)
}
//输出
//第一个参数
//第一个参数
//第一个参数
//第一个参数
//user-a
//user-b
//user-c
//user-d
通过运行结果可以看出,第三个参数定义的规则并没有执行。这是因为reduce的第三个参数是在使用parallelStream的reduce操作时,合并各个流结果的,本例中使用的是stream,所以第三个参数是不起作用的。上述示例,提供一个只有一个元素1的arrayList,通过累加器迭代,将stream中的数据添加到arrayList中。
参考:
[]: https://blog.csdn.net/weixin_43860260/article/details/94875064 “Reduce”
三、实例
统计一篇文章中出现最频繁的文字
fun add(char: Char) = char
val fileReader = FileReader(ClassUtil.getClassPath() + "text.txt")
val list = ArrayList<Char>()
var flat = 0
while (flat != -1) {
flat = fileReader.read()
val char = flat.toChar()
list.add(char)
}
fileReader.close()
val max = list
.stream()
.collect(Collectors.groupingBy(::add, Collectors.summingInt { 1 })) //根据字符分组,并统计个数
.filter { //过滤掉空白字符
it.key.toString().trim() != ""
}
.entries //获取实体集合
.sortedBy(Map.Entry<Char, Int>::value) //根据实体的value从小到大排序
.last() //取出最后一个实体
println(max)
统计一篇文章中出现最频繁的词(jieba分词)
fun add(string: String) = string
fun main(args: Array<String>) {
val fileReader = FileReader(ClassUtil.getClassPath() + "text.txt")
val text = fileReader.readText()
val engine = TokenizerUtil.createEngine()
val result = engine.parse(text)
val list = ArrayList<String>()
result.forEach {
list.add(it.text)
}
val max = list
.stream()
.collect(Collectors.groupingBy(::add, Collectors.summingInt { 1 })) //根据字符分组,并统计个数
.filter { //过滤掉空白字符
it.key.toString().trim() != "" && it.key.length >= 2
}
.entries //获取实体集合
.sortedBy(Map.Entry<String, Int>::value) //根据实体的value从小到大排序
.last()
println(max)
}