Java8的lambda表达式和Stream API
一直在用JDK8 ,却从未用过Stream,为了对数组或集合进行一些排序、过滤或数据处理,只会写for循环或者foreach,这就是我曾经的一个写照。
刚开始写写是打基础,但写的多了,各种乏味,非过来人不能感同身受。今天,我就要分享一篇如何解决上述问题的新方法 - Stream API。但学习Stream之前却不得不学一下Lambda表达式。说实话,网上介绍Lambda表达式的文章很多,大多晦涩难懂,今天我就想用自己的理解去说一下Lambda表达式是如何让我们的代码写的更少!
02来自IDEA的提示
在IDE中,你是否遇到在写以下列代码时,被友情提示的情况:
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("thread");
}
});
这时候,我们按一下快捷键,IDE自动帮我们把代码优化为酱个样子:
new Thread(() -> System.out.println("thread"));
这就是Java8的新特性:lambda表达式。
03Lambda入门
借用上面的示例,在调用new Thread的含参构造方法时,我们通过匿名内部类的方式实现了Runnable对象,但其实有用的代码只有System.out.println("thread")这一句,而我们却要为了这一句去写这么多行代码。正是这个问题,才有了Java8中的lambda表达式。那lambd表达式究竟是如何简化代码的呢?
先来看lambda表达式的语法:
() -> {}
(): 括号就是接口方法的括号,接口方法如果有参数,也需要写参数。只有一个参数时,括号可以省略。
->: 分割左右部分的,没啥好说的。
{} : 要实现的方法体。只有一行代码时,可以不加括号,可以不写return。
看了上面的解释,也就不难理解IDE优化后的代码了。不过看到这里你也许意识到,如果接口中有多个方法时,按照上面的逻辑lambda表达式恐怕不行了。没错,lambda表达式只适用于函数型接口。说白了,函数型接口就是只有一个抽象方法的接口。这种类型的接口还有一个对应的注解:@FunctionalInterface。为了让我们在需要这种接口时不再自己去创建,Java8中内置了四大核心函数型接口。
消费型接口(有参无返回值) Consumer<T> void accept(T t);
供给型接口(无参有返回值) Supplier<T> T get();
函数型接口(有参有返回值) Function<T, R> R apply(T t);
断言型接口(有参有布尔返回值) Predicate<T> boolean test(T t);
看到这里如果遇到一般的lambda表达式,你应该可以从容面对了,但高级点的恐怕看到还是懵,不要急,其实也不难。Lambda表达式还有两种简化代码的手段,分别是方法引用、构造引用。
04方法引用
方法引用是什么呢?如果我们要实现接口的方法与另一个方法A类似,(这里的类似是指参数类型与返回值部分相同),我们直接声明A方法即可。也就是,不再使用lambda表达式的标准形式,改用高级形式。无论是标准形式还是高级形式,都是lambda表达式的一种表现形式。
举例:
Function function1 = (x) -> x;
Function function2 = String::valueOf;
对比Function接口的抽象方法与String的value方法,可以看到它们是类似的。
R apply(T t); public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
方法引用的语法:
对象::实例方法
类::静态方法
类::实例方法
前两个很容易理解,相当于对象调用实例方法,类调用静态方法一样。只是第三个需要特殊说明。
当出现如下这种情况时:
Compare<Boolean> c = (a, b) -> a.equals(b);
用lambda表达式实现Compare接口的抽象方法,并且方法体只有一行,且该行代码为参数1调用方法传入参数2。此时,就可以简化为下面这种形式:
Compare<Boolean> c = String::equals;
也就是“类::实例方法”的形式。
值得一提的是,当参数b不存在时,该方式依旧适用。例如:
Function function1 = (x) -> x.toString();
Function function1 = Object::toString;
05构造引用
先来创建一个供给型接口对象:
Supplier<String> supplier = () -> new String();
在这个lammbda表达式中只做了一件事,就是返回一个新的String对象,而这种形式可以更简化:
Supplier<String> supplier = String::new;
提炼一下构造引用的语法: 类名::new
当通过含参构造方法创建对象,并且参数列表与抽象方法的参数列表一致,也就是下面的这种形式: Function1 function = (x) -> new String(x);
也可以简化为: Function1 function = String::new;
特殊点的数组类型: Function<Integer,String[]> function = (x) -> new String[x];
可以简化为:
Function<Integer,String[]> function = String[]::new;
06Lambda总结
上面并没有给出太多的lambda实例,只是侧重讲了如何去理解lambda表达式。到这里,不要懵。要记住lambda的本质:为函数型接口的匿名实现进行简化与更简化。
所谓的简化就是lambda的标准形式,所谓的更简化是在标准形式的基础上进行方法引用和构造引用。
方法引用是拿已有的方法去实现此刻的接口。
构造引用是对方法体只有一句new Object()的进一步简化。
07Stream理解
如何理解Stream?在我看来,Stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的 Iterator。简单来说,它的作用就是通过一系列操作将数据源(集合、数组)转化为想要的结果。
Stream有三点非常重要的特性:
Stream 是不会存储元素的。
Stream 不会改变原对象,相反,他们会返回一个持有结果的新Stream。
Stream 操作是延迟执行的。意味着它们会等到需要结果的时候才执行。
08Stream生成
Collection系的 stream() 和 parallelStream()
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream();
Stream<String> stringStream = list.parallelStream();
通过Arrays.stram()
Stream<String> stream1 = Arrays.stream(new String[10]);
通过Stream.of()
Stream<Integer> stream2 = Stream.of(1, 2, 3);
通过Stream.iterate()生成无限流
Stream<Integer> iterate = Stream.iterate(0, (x) -> x + 2);
iterate.limit(10).forEach(System.out::println);
通过Stream.generate()
Stream<Double> generate = Stream.generate(() -> Math.random());generate.forEach(System.out::println);
09Stream中间操作
多个中间操作连接而成为流水线,流水线不遇到终止操作是不触发任何处理的,所为又称为“惰性求值”。
list.stream()
.map(s -> s + 1) //映射
.flatMap(s -> Stream.of(s)) //和map差不多,但返回类型为Stream,类似list.add()和list.addAll()的区别
.filter(s -> s < 1000) //过滤
.limit(5) //限制
.skip(1) //跳过
.distinct() //去重
.sorted() //自然排序
.sorted(Integer::compareTo) //自定义排序
关于map方法,参数为一个Function函数型接口的对象,也就是传入一个参数返回一个对象。这个参数就是集合中的每一项。类似Iterator遍历。其它的几个操作思想都差不多。
执行上面的方法没什么用,因为缺少终止操作。
10Stream的终止操作
常用的终止API如下:
list.stream().allMatch((x) -> x == 555); // 检查是否匹配所有元素
list.stream().anyMatch(((x) -> x>600)); // 检查是否至少匹配一个元素
list.stream().noneMatch((x) -> x>500); //检查是否没有匹配所有元素
list.stream().findFirst(); // 返回第一个元素
list.stream().findAny(); // 返回当前流中的任意一个元素
list.stream().count(); // 返回流中元素的总个数
list.stream().forEach(System.out::println); //内部迭代
list.stream().max(Integer::compareTo); // 返回流中最大值
Optional<Integer> min = list.stream().min(Integer::compareTo);//返回流中最小值
System.out.println("min "+min.get());
reduce (归约):将流中元素反复结合起来得到一个值
Integer reduce = list.stream()
.map(s -> (s + 1))
.reduce(0, (x, y) -> x + y); //归约:0为第一个参数x的默认值,x是计算后的返回值,y为每一项的值。
System.out.println(reduce); Optional<Integer> reduce1 = list.stream()
.map(s -> (s + 1))
.reduce((x, y) -> x + y); // x是计算后的返回值,默认为第一项的值,y为其后每一项的值。
System.out.println(reduce);
collect(收集):将流转换为其他形式。需要Collectors类的一些方法。
//转集合
Set<Integer> collect = list.stream()
.collect(Collectors.toSet()); List<Integer> collect2 = list.stream()
.collect(Collectors.toList()); HashSet<Integer> collect1 = list.stream()
.collect(Collectors.toCollection(HashSet::new)); //分组 {group=[444, 555, 666, 777, 555]}
Map<String, List<Integer>> collect3 = list.stream()
.collect(Collectors.groupingBy((x) -> "group"));//将返回值相同的进行分组
System.out.println(collect3); //多级分组 {group={777=[777], 666=[666], 555=[555, 555], 444=[444]}}
Map<String, Map<Integer, List<Integer>>> collect4 = list.stream()
.collect(Collectors.groupingBy((x) -> "group", Collectors.groupingBy((x) -> x)));
System.out.println(collect4); //分区 {false=[444], true=[555, 666, 777, 555]}
Map<Boolean, List<Integer>> collect5 = list.stream()
.collect(Collectors.partitioningBy((x) -> x > 500));
System.out.println(collect5); //汇总
DoubleSummaryStatistics collect6 = list.stream()
.collect(Collectors.summarizingDouble((x) -> x));
System.out.println(collect6.getMax());
System.out.println(collect6.getCount()); //拼接 444,555,666,777,555
String collect7 = list.stream()
.map(s -> s.toString())
.collect(Collectors.joining(","));
System.out.println(collect7);
java8 新特性 Stream流 分组 排序 过滤 多条件去重
public class Streams { private static List<User> list = new ArrayList<User>(); public static void main(String[] args) {
list = Arrays.asList(
new User(1, "a", 10),
new User(4, "d", 19),
new User(5, "e", 13),
new User(2, "b", 14),
new User(3, "a", 10),
new User(6, "f", 16)
); long start = System.currentTimeMillis(); order(); println(String.format("耗时[%s]毫秒", (System.currentTimeMillis() - start))); } /**
* 多条件去重
* @param list
*/
public static void order() {
list.stream().collect(Collectors.collectingAndThen(
Collectors.toCollection(() -> new TreeSet<>(
Comparator.comparing(o -> o.getAge() + ";" + o.getName()))), ArrayList::new)).forEach(u -> println(u));
} public static void group() {
Map<Integer, List<User>> collect = list.stream().collect(Collectors.groupingBy(User::getAge));
System.out.println(collect);
} /**
* filter过滤
* @param list
*/
public static void filterAge() {
list.stream().filter(u -> u.getAge() == 10).forEach(u -> println(u));
} /**
* sorted排序
*/
public static void stord() {
list.stream().sorted(Comparator.comparing(u-> u.getAge())).forEach(u -> println(u)); }
/**
* limit方法限制最多返回多少元素
*/
public static void limit() {
list.stream().limit(2).forEach(u -> println(u));
}
/**
* 不要前多n个元素,n大于满足条件的元素个数就返回空的流
*/
public static void skip() {
list.stream().skip(2).forEach(u -> println(u));
}
// 最大值 最小值
public static void statistics() {
Optional<User> min = list.stream().min(Comparator.comparing(User::getUserId));
println(min);
Optional<User> max = list.stream().max(Comparator.comparing(User::getUserId));
println(max);
} // 统计
public static void summarizingInt(){
IntSummaryStatistics statistics = list.stream().collect(Collectors.summarizingInt(User::getAge));
double average = statistics.getAverage();
long count = statistics.getCount();
int max = statistics.getMax();
int min = statistics.getMin();
long sum = statistics.getSum();
println(average);
println(count);
println(min);
println(sum);
println(max); }
/**
* 转set
*/
public static void toSet() {
Set<User> collect = list.stream().collect(Collectors.toSet());
Iterator<User> iterator = collect.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next().getUserId());
}
} /**
* 转map
*/
public static void toMap() {
Map<Integer, User> collect = list.stream().collect(Collectors.toMap(User::getUserId, u -> u));
for (Integer in : collect.keySet()) {
User u = collect.get(in);//得到每个key多对用value的值
println(u);
}
}
/**
*map
*/
public static void map() {
list.stream().map(User::getUserId).forEach(userId -> println(userId));
list.stream().mapToInt(User::getAge).forEach(userId -> println(userId));
list.stream().mapToDouble(User::getUserId).forEach(userId -> println(userId));
list.stream().mapToLong(User::getUserId).forEach(userId -> println(userId));
} /**
* 查找与匹配
* allMatch方法与anyMatch差不多,表示所有的元素都满足才返回true。noneMatch方法表示没有元素满足
*/
public static void anyMatch() {
boolean anyMatch = list.stream().anyMatch(u -> u.getAge() == 100);
boolean allMatch = list.stream().allMatch(u -> u.getUserId() == 10);
boolean noneMatch = list.stream().noneMatch(u -> u.getUserId() == 10);
println(anyMatch);
println(allMatch);
println(noneMatch);
} /**
* reduce操作
*/
public static void reduce() {
Optional<Integer> sum = list.stream().map(User::getAge).reduce(Integer::sum);
Optional<Integer> max = list.stream().map(User::getAge).reduce(Integer::max);
Optional<Integer> min = list.stream().map(User::getAge).reduce(Integer::min);
println(sum);
println(max);
println(min);
}
/**
* 公共输出
* @param c
*/
public static void println(Object c) {
System.out.println(c.toString());
} static class User {
private Integer userId; private String name; private Integer age; User(Integer userId, String name, Integer age) {
super();
this.userId = userId;
this.name = name;
this.age = age;
} public Integer getUserId() {
return userId;
} public void setUserId(Integer userId) {
this.userId = userId;
} public String getName() {
return this.name;
} public void setName(String name) {
this.name = name;
} public Integer getAge() {
return age;
} public void setAge(Integer age) {
this.age = age;
} @Override
public String toString() {
return "[userId=" + userId + ", name=" + name + ", age=" + age + "]";
} }
Java8的lambda表达式和Stream API的更多相关文章
- 十分钟学会Java8:lambda表达式和Stream API
Java8 的新特性:Lambda表达式.强大的 Stream API.全新时间日期 API.ConcurrentHashMap.MetaSpace.总得来说,Java8 的新特性使 Java 的运行 ...
- 十分钟学会Java8的lambda表达式和Stream API
01:前言一直在用JDK8 ,却从未用过Stream,为了对数组或集合进行一些排序.过滤或数据处理,只会写for循环或者foreach,这就是我曾经的一个写照. 刚开始写写是打基础,但写的多了,各种乏 ...
- java8中Lambda表达式和Stream API
一.Lambda表达式 1.语法格式 Lambda是匿名函数,可以传递代码.使用“->”操作符,改操作符将lambda分成两部分: 左侧:指定了 Lambda 表达式需要的所有参数 右侧:指定了 ...
- java8新特性-lambda表达式和stream API的简单使用
一.为什么使用lambda Lambda 是一个 匿名函数,我们可以把 Lambda表达式理解为是 一段可以传递的代码(将代码像数据一样进行传递).可以写出更简洁.更灵活的代码.作为一种更紧凑的代码风 ...
- JDK1.8新特性(一) ----Lambda表达式、Stream API、函数式接口、方法引用
jdk1.8新特性知识点: Lambda表达式 Stream API 函数式接口 方法引用和构造器调用 接口中的默认方法和静态方法 新时间日期API default Lambda表达式 L ...
- Java8中Lambda表达式的10个例子
Java8中Lambda表达式的10个例子 例1 用Lambda表达式实现Runnable接口 //Before Java 8: new Thread(new Runnable() { @Overri ...
- JDK1.8中的Lambda表达式和Stream
1.lambda表达式 Java8最值得学习的特性就是Lambda表达式和Stream API,如果有python或者javascript的语言基础,对理解Lambda表达式有很大帮助,因为Java正 ...
- Java8 新特性学习 Lambda表达式 和 Stream 用法案例
Java8 新特性学习 Lambda表达式 和 Stream 用法案例 学习参考文章: https://www.cnblogs.com/coprince/p/8692972.html 1.使用lamb ...
- java8的lambda表达式,将List<DTO> 转为 List<DO>
将List<PhoneDTO>转为List<PhoneDO>,通过java8的lambda表达式来操作,比传统的for循环精简很多: /** * List<PhoneDT ...
随机推荐
- EventTrigger动态添加监听事件
在 Unity3D 中,通过拖拽的方式在 EventTrigger 组件中添加监听事件就不多说了,很简单.这里主要说的是通过代码动态往 EventTrigger 组件中添加监听事件,有个很坑的地方,就 ...
- 2.3.7synchronized代码块有volatile同步的功能
关键字synchronized可以使多个线程访问同一个资源具有同步性,而且他还具有将线程工作内存中的私有变量与公共内存中的变量同步的功能. package com.cky.thread; /** * ...
- android-基础编程-ScrollView
滚动视图(ScrollView)是指当拥有很多内容,屏幕显示不完时,需要通过滚动来显示完整的视图.包括水平滚动视图(HorizontalScrollView)和垂直滚动视图(ScrollView) 基 ...
- Vuejs——(6)Vuejs与form元素
版权声明:出处http://blog.csdn.net/qq20004604 目录(?)[+] 资料来于官方文档: http://cn.vuejs.org/guide/forms.html 本 ...
- 用react+redux写一个todo
概述 最近学习redux,打算用redux写了一个todo.记录下来,供以后开发时参考,相信对其他人也有用. 代码 代码请见我的github 组织架构如下图:
- HttpWebRequst中https的验证处理问题
最近在公司项目中使用了HttpWebRequst相关API,运行环境为.Net/Mono 2.0,是一款针对Unity平台的工具.开发过程中碰到了大家可能都碰到过的问题,Http还是Https? 为什 ...
- 可能比文档还详细--VueRouter完全指北
可能比文档还详细--VueRouter完全指北 前言 关于标题,应该算不上是标题党,因为内容真的很多很长很全面.主要是在官网的基础上又详细总结,举例了很多东西.确保所有新人都能理解!所以实际上很多东西 ...
- web自动化测试---自动化脚本设置百度搜索每页显示条数
前面学的都是基础知识,本篇将进入实战练习 以百度“搜索设置”为对象进行测试用例的写作: 百度的搜索设置在首页的“设置”里面,鼠标悬停之后即可显示,如下图红框位置: 测试目标是,修改每页的显示条数为50 ...
- Android并发编程 开篇
该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...
- salesforce lightning零基础学习(十) Aura Js 浅谈三: $A、Action、Util篇
前两篇分别介绍了Component类以及Event类,此篇将会说一下 $A , Action以及 Util. 一. Action Action类通常用于和apex后台交互,设置参数,调用后台以及对结 ...