真的是好久没有来写了,今天来总结一下传说中的lambda和stream
lambda表达式和函数式接口
stream
允许把函数当做参数传递给某个方法,或者把代码本身当作数据处理;(行为参数化)
(parameters)->expression 或者 (parameters)->{statements;}
简单的例子:
() -> 5;
(int x,int y) -> x-y;
(String s) -> System.out.println(s);
可选类型声明:不需要声明参数类型,编译器能够推断出参数的类型。
可选的参数圆括号:只有一个参数的时候可以不用圆括号,但是多个参数的时候需要定义。
可选的大括号:如果主体只包含一个语句,就不需要使用大括号。
可选的返回关键字
在lambda表达式中,只能引用标记为final的外层局部变量,也就是说在lambda表达式内部不可以修改外层局部变量,在外部也不可以改变,否则会编译错误。
int portNumber = 1122;
Runnable r = () -> System.out.println(portNumber);
portNumber = 3222;
lambda表达式的体和嵌套块有相同的作用域,所以不允许声明一个与局部变量同名的参数或者局部变量。
String first = ""; Comparator<String > comparator = (first,second) -> Integer.compare(first.length(), second.length());
只有一个抽象方法的接口,可以有默认方法(default关键字)和静态方法,这样的接口可以隐式转换为lambda表达式。函数式接口一般用注解@Functionallnterface标注。默认方法和抽象方法之间的区别在于抽象方法需要实现,而默认方法不需要,默认方法使得开发者可以在不破坏二进制兼容性的前提下,往现存接口中添加新的方法。
四个重要的函数式接口:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6fl0Zkeb-1569503124364)(D:\workfile\学习资料\Java学习\markdown\图片\函数式接口.png)]
使用()-> {} 替换匿名内部类
使用匿名内部类的方式实现:
List words = Arrays.asList("apple", "banana", "pear");
words.sort(new Comparator() {
@Override
public int compare(String w1, String w2) {
return Integer.compare(w1.length(), w2.length());
}
});
使用lambda表达式:
words.sort(( w1, w2) -> Integer.compare(w1.length(), w2.length()));
以流水线的方式处理数据
List integers = Arrays.asList(4, 5, 6,1, 2, 3,7, 8,8,9,10);
List evens = integers.stream().filter(i -> i % 2 == 0)
.collect(Collectors.toList()); //过滤出偶数列表 [4,6,8,8,10]
List sortIntegers = integers.stream().sorted()
.limit(5).collect(Collectors.toList());//排序并且提取出前5个元素 [1,2,3,4,5]
用内部迭代取代外部迭代
内部迭代时,项目可以透明地并行处理,或者用更优化的顺序进行处理,外部迭代的方法很难做到。
List features = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API");
for (String feature : features) {
System.out.println(feature); //外部迭代
}
List features = Arrays.asList("Lambdas", "Default Method", "Stream API",
"Date and Time API");
features.stream.forEach(n -> System.out.println(n)); //内部迭代
重构现有臃肿代码
排序
public class AppleCpmparator implements Comparatot<Apple>
{
public int compare(Apple a1,Apple a2)
{
return a1.getWeight().compareTo(a2.getWeight());
}
}
inventory.sort(new AppleComparator());
inventory.sort(new AppleComparator<Apple>()
{
public int compare(Apple a1,Apple a2)
{
return a1.getWeight().compareTo(a2.getWeight());
}
});
inventory.sort((a1,a2)>a1.getWeight().compaerTo(a2.getWeight()));
Comparator 具有一个叫做comparing的静态辅助方法,它可以接受一个Function来提取Comparable键值,并生成一个Comparator对象。所以可以把代码修改的更加紧凑一些:
inventory.sort(comparing((a) -> a.getWeight()));
inventory.sort(comparing(Apple::getWeight));
能够对集合对象进行各种串行或并行聚合的操作,比如排序、过滤、重组和数据统计等操作。
集合和流的差异在于什么时候进行计算,集合是一个内存中的数据结构,它包含数据结构中目前所有的值(集合中的每个元素都得先计算出来才能添加到集合中)。流则是在概念上固定的数据结构,不能添加或删除元素,其元素是按需计算的。流只能遍历一次,遍历完成后,就说这个流已经被消费掉了。
每一个stream都有两种模式:顺序执行和并行执行
顺序:
List list = Arrays.asList("a", "b", "c", "d");
Stream listStream = list.stream();
并行:
Stream parallelListStream = list.parallelStream();
使用顺序方式去遍历时,每个item读完后再去读下一个item
使用并行方式去遍历时,数组会被分成多个段,其中每一个都在不同的线程中处理,然后将结果一起输出。
通过collection接口和数组的默认方法
ArrayList 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 stream = users.stream()
通过stream接口的静态工厂方法(of、empty)
Stream stream = Stream.of(1, 2, 3, 4);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7nWDlY92-1569503124366)(D:\workfile\学习资料\Java学习\markdown\图片\stream操作.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nCIBB6Fu-1569503124366)(D:\workfile\学习资料\Java学习\markdown\图片\stream中间操作.png)]
结束操作:一个流只能执行一个结束操作,当执行了结束操作以后这个流就不能再被执行。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Xv5uhONV-1569503124367)(D:\workfile\学习资料\Java学习\markdown\图片\stream结束操作.png)]
filter 函数需要传入一个实现predicate函数式接口的对象,该接口的抽象方法test接收一个参数并返回一个booleann值,为true则保留,false则删除。
List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
numbers.stream()
.filter(i -> i % 2 == 0)
.distinct()
.forEach(System.out::println);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YI7ELdiS-1569503124367)(D:\workfile\学习资料\Java学习\markdown\图片\数据流.png)]
一对一的映射,每输入一个数据也只会输出一个值。
给定一个单词表,想要返回另一个列表,显示每个单词中有几个字母
List<String> words = Arrays.asList("Hello", "World");
List<Integer> wordLengths = words.stream()
.map(String::length)
.collect(toList());
一对多的映射,对每个元素映射出来的依旧是一个stream
给定一张单词表,如何返回一张列表,列出里面各不相同的字符,例如:给定单词表[‘Hello’,“World”],返回[“H”,“e”,“l”,“o”,“W”,“r”,“d”]
先看看map的解决方法:
List<String> words = Arrays.asList("Hello", "World");
words.stream()
.map(word->word.split(""))
.distinct()
.collect(Collectors.toList());
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dOT9Pvys-1569503124367)(D:\workfile\学习资料\Java学习\markdown\图片\map.png)]
flatmap的解决方法:
flatmap把一个流中的每个值都换成另一个流,然后把所有的流连接起来成为一个流。
words.stream().map(w->w.split(""))
.flatMap(Arrays::stream)
.distinct()
.collect(Collectors.toList());
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YiL8ujGG-1569503124368)(D:\workfile\学习资料\Java学习\markdown\图片\flatmap.png)]