- 浏览: 31558 次
文章分类
最新评论
lambda和stream
lambda表达式
Java8引入了lambda表达式,在其他语言中,比如python、swift都支持lambda表达式,这个特性用起来也非常方便和简洁。 先来看lambda表达式的语法:
() -> {}
() : 括号就是接口方法的括号,接口方法如果有参数,也需要写参数。只有一个参数时,括号可以省略。
-> : 分割形参列表与函数体的。
{} : 如果代码体只有一行代码就可以省略掉花括号,并且如果方法需要有返回值连return关键词都可以省略,系统会自动将这一行代码的结果返回
例子:
public class HelloWorld
{
public static void main(String[] args)
{
ArrayList<String> list = new ArrayList<>();
list.add("Objective-C");
list.add("Swift");
list.add("Python");
list.add("Golang");
list.add("Java");
list.sort((s1, s2)->s1.compareTo(s2));
list.forEach(System.out::println);
}
}
/**Golang
Java
Objective-C
Python
Swift**/
上面的代码我们省略了形参的类型,由于只有一行我们同时省略了花括号和return语句,整个代码相比使用匿名内部类更加简洁了。 lambda怎么知道实现的是接口的哪一个方法 lambda表达式的类型也被称为目标类型 target type,该类型必须是函数式接口 Functional Interface,函数式接口代表有且只有一个抽象方法,但是可以包含多个默认方法或类方法的接口,因此使用lambda表达式系统一定知道我们实现的接口的哪一个方法,因为实现的接口有且只有一个抽象方法供我们实现。
函数式接口可以使用注释**@FunctionalInterface**来要求编译器在编译时进行检查,是否只包含一个抽象方法。Java提供了大量的函数式接口这样就能使用lambda表达式简化编程。lambda表达式的目标类型必须是函数式接口,lambda表达式也只能为函数式接口创建对象因为lambda表达式只能实现一个抽象方法。
lambda表达式提供了四种引用方法和构造器的方式:
引用对象的方法 类::实例方法
引用类方法 类::类方法
引用特定对象的方法 特定对象::实例方法
引用类的构造器 类::new
举例:
public class HelloWorld
{
public static void main(String[] args)
{
ArrayList<String> list = new ArrayList<>();
list.add("Objective-C");
list.add("Swift");
list.add("Python");
list.add("Golang");
list.add("Java");
//list.sort((s1, s2)->s1.compareTo(s2));
list.sort(String::compareTo);
list.forEach(System.out::println);
}
}
对比上述两行代码,第一个sort函数传入了一个lambda表达式用于实现Comparator接口的compare函数,由于该实现只有一条代码,因此可以省略花括号以及return关键字。第二个sort方法则直接引用了对象的实例方法,语法规则为==类::实例方法==,系统会自动将函数式接口实现的方法的所有参数中的第一个参数作为调用者,接下来的参数依次传入引用的方法中即自动进行s1.compareTo(s2)的方法调用,明显第二个sort函数调用更加简洁明了。
最后一行代码list.forEach(System.out::println);则引用了==类方法==,集合类的实例方法forEach接收一个Consumer接口对象,该接口是一个函数式接口,只有一个抽象方法void accept(T t);,因此可以使用lambda表达式进行调用,这里引用System.out的类方法println,引用语法类::类方法,系统会自动将实现的函数式接口方法中的所有参数都传入该类方法并进行自动调用。
总的来说lambda的本质就是为函数型接口的匿名实现进行简化与更简化。 所谓的简化就是lambda的标准形式,所谓的更简化是在标准形式的基础上进行方法引用和构造引用。 方法引用是拿已有的方法去实现此刻的接口。 构造引用是对方法体只有一句new Object()的进一步简化。
Stream API
Stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的 Iterator。简单来说,它的作用就是通过一系列操作将数据源(集合、数组)转化为想要的结果。 栗子:
public class User {
private String userID;
private boolean isVip;
private int balance;
public User(String userID, boolean isVip, int balance)
{
this.userID = userID;
this.isVip = isVip;
this.balance = balance;
}
public boolean isVip()
{
return this.isVip;
}
public String getUserID()
{
return this.userID;
}
public int getBalance()
{
return this.balance;
}
}
public class HelloWord {
public static void main(String[] args) {
ArrayList<User> users = new ArrayList<>();
users.add(new User("2017001", false, 0));
users.add(new User("2017002", true, 36));
users.add(new User("2017003", false, 98));
users.add(new User("2017004", false, 233));
users.add(new User("2017005", true, 68));
users.add(new User("2017006", true, 599));
users.add(new User("2017007", true, 1023));
users.add(new User("2017008", false, 9));
users.add(new User("2017009", false, 66));
users.add(new User("2017010", false, 88));
//普通实现方式
ArrayList<User> tempArray = new ArrayList<>();
ArrayList<String> idArray = new ArrayList<>(3);
for (User user: users)
{
if (user.isVip())
{
tempArray.add(user);
}
}
tempArray.sort(new Comparator<User>(){
public int compare(User o1, User o2) {
return o2.getBalance() - o1.getBalance();
}
});
System.out.println(tempArray);
for (int i = 0; i < 3; i++)
{
idArray.add(tempArray.get(i).getUserID());
}
for (int i = 0; i < idArray.size(); i++)
{
System.out.println(idArray.get(i));
}
//Stream API实现方式
//也可以使用parallelStream方法获取一个并发的stream,提高计算效率
Stream<User> stream = users.stream();
List<String> array = stream.filter(User::isVip).sorted((t1, t2) -> t2.getBalance() - t1.getBalance()).limit(3).map(User::getUserID).collect(Collectors.toList());
array.forEach(System.out::println);
}
}
//结果
/**
2017007
2017006
2017005**/
上述代码首先定义了一个用户类,这个类保存用户是否是VIP、用户ID以及用户的余额,假如现在有一个需求,将VIP中余额最高的三个用户的ID找出来,传统的思路一般就是创建一个临时的list,然后逐一判断,将所有的VIP用户加入到这个临时的list中,然后调用集合类的sort方法根据余额排序,最后再遍历三次获取余额最高的三个用户的ID等信息。这样的方法看似简单,但代码写出来即混乱也不好看,如果用户量非常大,有几千万甚至几个亿,这样遍历的方式效率就会特别低,如果手工加上多线程的并发操作,代码就更加复杂了 。 上述代码的第二部分使用Stream API的方式来计算,首先通过集合类获取了一个普通的stream,如果数据量大可以使用parallelStream方法获取一个并发的stream,这样接下来的计算程序员不需要编写任何多线程代码系统会自动进行多线程计算。获取了stream以后首先调用filter方法找到是否为VIP用户然后对VIP用户进行排序操作,接下来限制只获取三个用户的信息,然后将用户映射为用户ID,最后将该stream转换为集合类,两种实现方式的结果完全一样,但是明显的采用Stream API的代码更加简洁易懂。
如何使用
Stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的 Iterator。简单来说,它的作用就是通过一系列操作将数据源(集合、数组)转化为想要的结果。
当我们要使用Stream API时,首先需要创建一个Stream对象,可以通过集合类的实例方法stream或parallelStream来获取一个普通的串行stream或是并行stream。也可以使用Stream、IntStream、LongStream或DoubleStream创建一个Stream对象,Stream是一个比较通用的流,可以代表任何引用数据类型,其他的则是指特定类型的流。最常用的就是通过一个集合类型来获取相应类型的Stream。
流的操作分为==中间操作 Intermediate==和==结束操作 Terminal==: 中间操作(Intermediate):一个流可以采用链式调用的方式进行数个中间操作,主要目的就是打开流然后对这个流进行各种过滤、映射、聚集、统计操作等,如上述代码中的filter、map操作等。每一个操作结束后都会返回一个新的流,并且这些操作都是lazy的,也就是在进行结束操作时才会真正的进行计算,一次遍历就计算出所有结果。 结束操作(Terminal):一个流只能执行一个结束操作,当执行了结束操作以后这个流就不能再被执行,也就是说不能再次进行中间操作或结束操作,所以结束操作一定是流的最后一个操作,如上述代码中的collect方法。当开始执行结束操作的时候才会对流进行遍历并且只一次遍历就计算出所有结果
Stream的创建
- 通过集合类创建 通过集合创建Stream的方法是我们最常用的,集合类的实例方法stream和parallelStream可以获取相应的流。
ArrayList<User> users = new ArrayList<>();
users.add(new User("2017001", false, 0));
users.add(new User("2017002", true, 36));
users.add(new User("2017003", false, 98));
Stream<User> stream = users.stream();
- 通过数组构造
String[] str = {"Hello World", "Jiaming Chen", "Zhouhang Cheng"};
Stream<String> stream = Stream.of(str);
- 通过单个元素构造
Stream<Integer> stream = Stream.of(1, 2, 3, 4);
- Stream与Array和Collection的转换 一般我们都会对Stream进行结束操作,用于获取一个数组或是集合类,通过数组和集合类创建Stream前面已经介绍了,这里介绍通过Stream获取数组或集合类。
String[] str = {"Hello World", "Jiaming Chen", "Zhouhang Cheng"};
Stream<String> stream = Stream.of(str);
String[] strArray = stream.toArray(String[]::new);
List<String> strList = stream.collect(Collectors.toList());
ArrayList<String> strArrayList = stream.collect(Collectors.toCollection(ArrayList::new));
Set<String> strSet = stream.collect(Collectors.toSet());
上面的代码分别将流转换为数组、List、ArrayList和Set类型
Stream 常用方法
- filter filter的栗子前面已经举过了,filter函数需要传入一个实现Predicate函数式接口的对象,该接口的抽象方法test接收一个参数并返回一个boolean值,为true则保留,false则剔除,前文举的栗子就是判断是否为VIP用户,如果是就保留,不是就剔除 原理如图所示:
- map、flatMap map的栗子前面已经举过了,map函数需要传入一个实现Function函数式接口的对象,该接口的抽象方法apply接收一个参数并返回一个值,可以理解为映射关系,前文举的栗子就是将每一个用户映射为一个userID。 原理如图所示: map方法是一个一对一的映射,每输入一个数据也只会输出一个值。 flatMap方法是一对多的映射,对每一个元素映射出来的仍旧是一个Stream,然后会将这个子Stream的元素映射到父集合中,栗子如下:
Stream<List<Integer>> inputStream = Stream.of(Arrays.asList(1), Arrays.asList(2, 3), Arrays.asList(4, 5, 6));
List<Integer> integerList = inputStream.flatMap((childList) -> childList.stream()).collect(Collectors.toList());
//将一个“二维数组”flat为“一维数组”
integerList.forEach(System.out::println);
-
limit、skip limit用于限制获取多少个结果,与数据库中的limit作用类似,skip用于排除前多少个结果。
-
sorted sorted的栗子前面也举过了,sorted函数需要传入一个实现Comparator函数式接口的对象,该接口的抽象方法compare接收两个参数并返回一个整型值,作用就是排序,与其他常见排序方法一致。
-
distinct distinct用于剔除重复,与数据库中的distinct用法一致
-
findFirst findFirst方法总是返回第一个元素,如果没有则返回空,它的返回值类型是Optional<T>类型,接触过swift的同学应该知道,这是一个可选类型,如果有第一个元素则Optional类型中保存的有值,如果没有第一个元素则该类型为空。
-
min、max min可以对整型流求最小值,返回OptionalInt。 max可以对整型流求最大值,返回OptionalInt。 这两个方法是结束操作,只能调用一次。
-
allMatch、anyMatch、noneMatch
allMatch:Stream中全部元素符合传入的predicate返回 true anyMatch:Stream中只要有一个元素符合传入的predicate返回 true noneMatch:Stream中没有一个元素符合传入的predicate返回 true
-
reduce reduce方法用于组合Stream元素,它可以提供一个初始值然后按照传入的计算规则依次和Stream中的元素进行计算,因此上文介绍的min、max都可以看做是reduce的一种实现。
IntStream is = IntStream.range(0, 10);
System.out.println(is.reduce(0, Integer::sum));
IntStream intStream = IntStream.range(0, 10);
System.out.println(intStream.reduce((o1, o2) -> o1 + o2));
Stream<String> stream = Stream.of("Hello", "World", "Jiaming", "Chen");
System.out.println(stream.reduce("", String::concat));
//结果
/**45
OptionalInt[45]
HelloWorldJiamingChen
**/
第一个IntStream调用的reduce方法设置了一个初始值,因此最终reduce计算的结果一定有值,该方法调用Integer的类方法sum用于计算Stream的总和。 第二个IntStream调用reduce方法时没有设置初始值,因此最终reduce计算的结果不一定有值,所以返回值类型是Optional类型,没有提供初始值时会自动将第一个和第二个元素先进行计算,但有可能不存在第一个或第二个元素,因此返回值是Optional类型。
转载于:https://my.oschina.net/u/4116634/blog/3073160
相关推荐
Lambda和Stream
### Lambda和Stream在Java 8中的应用 #### Lambda表达式概览 Lambda表达式是Java 8中引入的一项重要特性,极大地增强了Java语言的灵活性和表达能力。它允许程序员以简洁的方式定义匿名函数,即无需指定函数名称...
全网详解(波哥)Java8新特性(Lambda、Stream、LocalDate等)新特性 自学java的同行们应该都要学习java8的新特性,譬如:(Lambda、Stream、LocalDate等)!本人在学习java的时候看的是波哥的视频,确实讲的不错,很...
java8新特性Lambda与stream视频教程含文档
该PPT为本人翻遍大部分帖子、博客、资源等一系列资料自我整合了一套技术分享PPT,以Java为载体,概括了Lambda表达式的使用概念、函数式编程的思想,以及Stream流的各个方法的使用,Stream流的强大功能等。
其中,lambda 函数式接口和 Stream API 是两大核心特性,它们为 Java 带来了函数式编程的元素,使得代码更加简洁、易读。 ### Lambda 函数式接口 Lambda 表达式是 Java 8 的一大亮点,它是一种匿名函数,可以被...
Stream和Lambda表达式实践 在Java中,Stream API是Java 8中引入的一种新的数据处理方式,它可以对集合进行各种操作,如过滤、映射、聚合等。Lambda表达式是Java 8中引入的一种新的函数式编程方式,它可以将函数作为...
封装stream和lambda操作进行数据返回处理"这个主题中,我们将探讨如何利用MyBatis-Plus与Java 8的Stream API和Lambda表达式相结合,实现更加简洁和高效的数据库操作。 1. **MyBatis-Plus基础**: MyBatis-Plus提供...
Java Lambda 和 Stream API 是 Java 8 引入的两个重要特性,它们极大地改变了Java程序员处理集合数据的方式,提升了代码的简洁性和可读性。这里我们将深入探讨这两个概念以及它们在实际编程中的应用。 Lambda表达式...
Lambda_01.zip和Lambda_02.zip可能分别包含了不同的Lambda表达式和Stream API的案例代码,供学习者分析和实践。通过这些实践,开发者可以深入理解这些新特性,并将其应用到实际项目中,提升代码的可读性和效率。
在阿里云的实践中,Lambda和Stream的引入并没有导致性能下降,反而是通过重构,使得整体的代码质量和性能得到了优化。 除了Lambda和Stream,JDK8还带来了默认分层编译机制,这项技术的引入显著地加快了应用程序在...
Java Lambda 和 Stream API 是 Java 8 引入的两个重要特性,它们极大地简化了代码,提高了编程效率。Lambda 表达式是函数式编程的核心概念,而 Stream API 则为处理大量数据提供了一种高效、易读的方式。下面将详细...
Lambda与Stream API(处理方案示例).md
java.Lambda与Stream API(解决方案).md
其中,Stream API和Lambda表达式是最为显著的两大亮点,它们为处理集合数据提供了新的方式,并且简化了多线程编程。下面将详细讲解这两个特性。 **一、Stream API** Stream API是Java 8引入的一种处理数据的新概念...
【Java8】Lambda表达式 和 Stream API 是Java编程语言中的两个重要创新,它们极大地提升了代码的简洁性和可读性,特别是在处理集合数据时。这里我们将深入探讨这两个特性,并结合实际示例来理解它们的工作原理。 ...
java.Lambda与Stream API(处理方案示例).md
Lambda表达式和stream流的入门 java 响应式编程