1.3 Lambda中的stream
所谓 “Lambda 表达式”(lambda expression)它是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。Lambda表达式可以表示闭包(注意和数学传统意义上的不同)。如果想深入可以去这位仁兄的博客看看 点我进去 这里做个了解。
认准 " - > " Lambad 老字号
可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。
在 Lambda 表达式当中不允许声明一个与局部变量同名的参数或者局部变量。
String first = ""; Comparator<String> comparator = (first, second) -> Integer.compare(first.length(), second.length()); //编译会出错
从语法特征分析出的傻白甜代码片段
// 1. 不需要参数,返回值为 5 () -> 5 // 2. 接收一个参数(数字类型),返回其2倍的值 x -> 2 * x // 3. 接受2个参数(数字),并返回他们的差值 (x, y) -> x – y // 4. 接收2个int型整数,返回他们的和 (int x, int y) -> x + y // 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void) (String s) -> System.out.print(s)
public class Java8Tester { public static void main(String args[]){ Java8Tester tester = new Java8Tester(); // 类型声明 MathOperation addition = (int a, int b) -> a + b; // 不用类型声明 MathOperation subtraction = (a, b) -> a - b; // 大括号中的返回语句 MathOperation multiplication = (int a, int b) -> { return a * b; }; // 没有大括号及返回语句 MathOperation division = (int a, int b) -> a / b; System.out.println("10 + 5 = " + tester.operate(10, 5, addition)); System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction)); System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication)); System.out.println("10 / 5 = " + tester.operate(10, 5, division)); // 不用括号 GreetingService greetService1 = message -> System.out.println("Hello " + message); // 用括号 GreetingService greetService2 = (message) -> System.out.println("Hello " + message); greetService1.sayMessage("Runoob"); greetService2.sayMessage("Google"); } interface MathOperation { int operation(int a, int b); } interface GreetingService { void sayMessage(String message); } private int operate(int a, int b, MathOperation mathOperation){ return mathOperation.operation(a, b); } }
使用 ( ) - { } 来代替匿名类 代码来自这位仁兄 点我进去
//未使用Lambda
Thread t1 = new Thread(new Runnable() { @Override public void run() { System.out.println("no use lambda"); } });
//使用之后 Thread t2 = new Thread(() -> System.out.println("use lambda"));
我们看到相对而言Lambda表达式要比匿名类要优雅简洁很多~。
List<Integer> integers = Arrays.asList(4, 5, 6,1, 2, 3,7, 8,8,9,10); List<Integer> evens = integers.stream().filter(i -> i % 2 == 0) .collect(Collectors.toList()); //过滤出偶数列表 [4,6,8,8,10]<br> List<Integer> sortIntegers = integers.stream().sorted() .limit(5).collect(Collectors.toList());//排序并且提取出前5个元素 [1,2,3,4,5] List<Integer> squareList = integers.stream().map(i -> i * i).collect(Collectors.toList());//转成平方列表 int sum = integers.stream().mapToInt(Integer::intValue).sum();//求和 Set<Integer> integersSet = integers.stream().collect(Collectors.toSet());//转成其它数据结构比如set Map<Boolean, List<Integer>> listMap = integers.stream().collect(Collectors.groupingBy(i -> i % 2 == 0)); //根据奇偶性分组 List<Integer> list = integers.stream().filter(i -> i % 2 == 0).map(i -> i * i).distinct().collect(Collectors.toList());//复合操作
在java 8 中 Stream 不是集合元素,它不保存数据,它是有关算法和计算的,它更像一个高级版本的 Iterator。原始版本的 Iterator,用户只能显式地一个一个遍历元素并对其执行某些操作;高级版本的 Stream,用户只要给出需要对其包含的元素执行什么操作,比如 “过滤掉长度大于 10 的字符串”、“获取每个字符串的首字母”等,Stream 会隐式地在内部进行遍历,做出相应的数据转换。
Stream 就如同一个迭代器(Iterator),单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了,就好比流水从面前流过,一去不复返。而和迭代器又不同的是,Stream 可以并行化操作,迭代器只能命令式地、串行化操作。顾名思义,当使用串行方式去遍历时,每个 item 读完后再读下一个 item。而使用并行去遍历时,数据会被分成多个段,其中每一个都在不同的线程中处理,然后将结果一起输出。Stream 的并行操作依赖于 Java7 中引入的 Fork/Join 框架(JSR166y)来拆分任务和加速处理过程。
甚至函数 由值创建流:
Stream<String> stream = Stream.of("Java 8 ", "Lambdas ", "In ", "Action");
由数组创建流:
int[] numbers = {2, 3, 5, 7, 11, 13}; int sum = Arrays.stream(numbers).sum();
外部迭代:描述怎么干,代码里嵌套2个以上的for循环的都比较难读懂;只能顺序处理List中的元素;
内部迭代:描述要干什么,而不是怎么干;不一定需要顺序处理List中的元素
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)); //内部迭代
Lambda的并行流虽好,但也要注意使用场景。如果平常的业务处理比如过滤,提取数据,没有涉及特别大的数据和耗时操作,则真的不需要开启并行流。我在工作中看到有些人一个只有几十个元素的列表的过滤操作也开启了并行流,其实这样做会更慢。因为多行线程的开启和同步这些花费的时间往往比你真实的处理时间要多很多。但一些耗时的操作比如I/O访问,DB查询,远程调用,这些如果可以并行的话,则开启并行流是可提升很大性能的。因为并行流的底层原理是fork/join,如果你的数据分块不是很好切分,也不建议开启并行流。举个例子ArrayList的Stream可以开启并行流,而LinkedList则不建议,因为LinkedList每次做数据切分要遍历整个链表,这本身就已经很浪费性能,而ArrayList则不会。
有篇来自 发布在 jaxenter 的调查报告 《How misusing Streams can make your code 5 times slower》
原文地址 点我进去 中文翻译版 点我进去 此文用代码实验得出了一个结论 如果你要进行stream操作 使用 iterators and for-each 效率更高
大家的文章各有优长 取其所长 感谢各位大大的文章啊 = =
原文:https://www.cnblogs.com/hwcs/p/10481021.html