“Lambda 表达式”是一个匿名函数,它能够包含表达式和语句,而且可用于建立委托或表达式树类型.
全部 Lambda 表达式都使用 Lambda 运算符 =>,该运算符读为“goes to”.该 Lambda 运算符的左边是输入参数(若是有),右边包含表达式或语句块.Lambda 表达式 x => x * x 读做“x goes to x times x”.能够将此表达式分配给委托类型提起对于集合的遍历,恐怕下面的这种方式已是一种思惟定式了吧:php
final List<String> friends = Arrays.asList("Brian", "Nate", "Neal", "Raju", "Sara", "Scott"); for(int i = 0; i < friends.size(); i++) { System.out.println(friends.get(i)); }
可是仔细想一想,以上的代码彷佛出现了过多的细节,好比循环变量i的出现。在作简单的遍历操做时,循环变量其实是没必要要的,只有在对某个特定位置的元素执行某个特殊操做时,循环变量的使用才有意义。因此,在Java中引入了加强的for循环,在这种循环方式中,循环变量是没必要要的:java
for(String name : friends) { System.out.println(name); }
这种方式,在实现细节上使用的是iterator接口和它的hasNext(),next()方法。闭包
不管使用哪一种for循环,它们仍然使用了外部遍历器(External Iterator)。即在for循环中,你老是有办法经过诸如break,continue等方式来控制遍历的过程。app
与外部遍历器相对的,是内部遍历器(Internal Iterator)。在Java 8中,Iterable接口被加强了,如今该接口拥有一个forEach方法用来实现内部遍历器。forEach方法会接受一个Consumer接口类型做为参数,该接口是一个函数式接口(Functional Interface),它是内部遍历器的实现方式。关于函数式接口,能够参考上一篇文章。函数
friends.forEach(new Consumer<String>() { public void accept(final String name) { System.out.println(name); } });
很显然,上述代码中使用的匿名类在Java 8中并非最好的方案,在这种场景下Lambda表达式是更好的选择:工具
friends.forEach((final String name) -> System.out.println(name));
forEach方法自己是一个高阶函数,由于它接受了一个Lambda表达式做为其参数,而Lambda表达式在本质上则是一个函数。在Lambda表达式的左边,声明了一个String类型的变量name,它表明了集合中的元素。而箭头右边的代码则表达了对于该元素应该执行何种操做。forEach之因此被称为内部遍历器,缘由在于一旦它开始执行了,那么遍历操做就不可以被轻易中断。oop
同时,借助Java编译器的类型推导(Type Inference)特性,Lambda表达式可以被进一步简化以下:学习
friends.forEach((name) -> System.out.println(name));
此时,编译器可以经过运行时的上下文知道这个name变量的类型是String。测试
另外,当Lambda表达式左端只接受一个变量的时候,括号也是能够省略的:网站
friends.forEach(name -> System.out.println(name));
可是用类型推导有一个很差的地方,就是参数不会自动被final修饰。所以,在Lambda表达式右端,是能够对参数进行修改的,然而这种行为是不被倡导的。
上面的代码已经足够简洁了,可是还有更简洁的方法,那就是使用方法引用:
friends.forEach(System.out::println);
使用这种方式甚至不须要写出Lambda表达式的左端参数部分。关于方法引用的详细状况,会在之后进行介绍。
与使用外部遍历不一样,使用内部遍历的好处在于:
将一个集合经过某种计算获得另外一个集合是一种经常使用的操做,也是Lambda表达式的用武之地。 好比,以将一个名字集合转换为首字母大写了的名字集合为例。
为了避免改变原集合,最“天然”的方式以下:
final List<String> uppercaseNames = new ArrayList<String>(); for(String name : friends) { uppercaseNames.add(name.toUpperCase()); }
以上代码使用了外部遍历器,即for循环来完成集合操做。而将命令式代码转变为声明式代码(也就是函数式)的首要任务就是观察遍历的使用方式,尽量地将外部遍历更改成内部遍历:
final List<String> uppercaseNames = new ArrayList<String>(); friends.forEach(name -> uppercaseNames.add(name.toUpperCase())); System.out.println(uppercaseNames);
好了,如今咱们使用了forEach来代替for循环。可是感受代码并无变的简洁多少。 咱们可使用其余的函数式接口(Functional Interface)来实现集合的转换。事实上,map方法比forEach方法更胜任这一类转换工做:
friends.stream() .map(name -> name.toUpperCase()) .forEach(name -> System.out.print(name + " ")); System.out.println();
这里使用了一个新的方法叫作stream()。在Java 8中,全部的集合类型都拥有这个方法。该方法的返回值是一个Stream类型的实例,该实例将集合自己包含在内(即上述的friends集合被包含在了stream实例中)。
能够将它理解成一个创建在集合上的iterator,它提供了除了forEach以外的更加高级的方法,如上述的map()。map方法的做用在于,它可以将接受的一个输入序列转换成一个输出序列(即完成转换工做)。这也意味着map方法是存在返回值的,因此后续的forEach方法操做的集合便是map方法返回的集合。map也可连续操做如 list2.parallelStream().map(String::toUpperCase).map(obj->obj+"===").forEach(System.out::println);将第一个map操做完的值给第二个map继续操做 阿西吧也能够 .filter(过滤操做).filter(在次进行过滤操做)
集合的转换操做能够是任意的,好比须要获得每一个名字的长度:
friends.stream() .map(name -> name.length()) .forEach(count -> System.out.print(count + " ")); // 5 4 4 4 4 5
使用方法引用可以对上面的代码进行简化:
friends.stream() .map(String::toUpperCase) .forEach(name -> System.out.println(name));
回顾以前咱们提到过的,当一个方法接受函数式接口做为参数时,能够传入Lambda表达式或者方法/构造器的引用进行调用。而以上的String::toUpperCase就是一个方法应用。
注意到对该方法进行引用时,省略了其参数信息。这是由于Java编译器在为该方法引用生成实例时,会进行类型推导自动地将集合中的元素做为参数传入到该方法中。
好比,当咱们须要获得名字集合中全部以N开头的名字时,最“天然”的实现方式立刻就会反映以下:
final List<String> startsWithN = new ArrayList<String>(); for(String name : friends) { if(name.startsWith("N")) { startsWithN.add(name); } }
可是,咱们可让这一切变得更加简单和优雅:
final List<String> startsWithN = friends.stream() .filter(name -> name.startsWith("N")) .collect(Collectors.toList());
对于filter方法,它期待的参数是一个返回boolean类型的Lambda表达式。对于被操做的集合中的每一个元素而言,若是Lambda表达式返回的是true,那么就意味着filter后获得的stream实例中是包含该元素的,反之亦然。最后,能够经过调用stream实例的collect方法来将stream实例转换成一个List实例。
好比,当须要对不止一个集合进行操做时:
final long countFriendsStartN = friends.stream().filter(name -> name.startsWith("N")).count(); final long countComradesStartN = comrades.stream().filter(name -> name.startsWith("N")).count(); final long countEditorsStartN = editors.stream().filter(name -> name.startsWith("N")).count();
显而易见,Lambda表达式须要被重用。咱们能够将Lambda表达式给保存到一个变量中,就像Java处理其余任何类型的变量同样。问题来了?Lambda表达式的类型是什么呢,在Java这种静态类型语言中,咱们不能单单使用诸如var,val就来表明一个Lambda表达式。
对于filter方法接受的Lambda表达式,它是符合Predicate接口类型的,所以能够声明以下:
final Predicate<String> startsWithN = name -> name.startsWith("N"); final long countFriendsStartN = friends.stream().filter(startsWithN).count(); final long countComradesStartN = comrades.stream().filter(startsWithN).count(); final long countEditorsStartN = editors.stream().filter(startsWithN).count();
可是,问题又来了!若是在某些状况下须要检测的不是以N开头,而是以别的字母如B开头呢? 那么,就须要再建立一个Lambda表达式并保存到变量中:
final Predicate<String> startsWithN = name -> name.startsWith("N"); final Predicate<String> startsWithB = name -> name.startsWith("B"); final long countFriendsStartN = friends.stream().filter(startsWithN).count(); final long countFriendsStartB = friends.stream().filter(startsWithB).count();
显然,这并非长久之计。不能由于须要检测的首字母不一样,就建立额外的Lambda表达式。咱们须要它进行进一步的抽象。
第一种方法:
public static Predicate<String> checkIfStartsWith(final String letter) { return name -> name.startsWith(letter); }
经过一个带参数的方法来获得须要的Lambda表达式。这个方法就是传说中的高阶函数,由于它返回了一个Lambda表达式做为返回值,而Lambda表达式本质上是一个函数。
另外,这里也体现了Java 8中关于Lambda表达式的另一个特性:闭包和做用域。在以上返回的Lambda表达式中引用了一个letter变量,而这个letter变量则是checkIfStartsWith方法接受的参数,就像JavaScript等拥有闭包特性的语言那样,Java也具备这种特性了。
可是,在Java中利用闭包对变量进行访问时,有须要注意的问题。咱们只能访问被final修饰的变量或者本质上是final的变量。正如上面checkIfStartsWith声明的参数被final修饰那样。
这是由于,Lambda表达式可能在任什么时候候被执行,也可能被任何其余线程执行。因此为了保证不出现竞态条件(Race Condition),须要保证Lambda表达式中引用到的变量不会被改变。
final long countFriendsStartN = friends.stream().filter(checkIfStartsWith("N")).count(); final long countFriendsStartB = friends.stream().filter(checkIfStartsWith("B")).count();
利用上述能够根据要求动态生成Lambda表达式的高阶函数,就能够按照上面这个样子来进行代码重用了。
实际上,使用static来实现以上的高阶函数并非一个好主意。能够将做用域缩小一些:
final Function<String, Predicate<String>> startsWithLetter = (String letter) -> { Predicate<String> checkStartsWith = (String name) -> name.startsWith(letter); return checkStartsWith; };
startsWithLetter变量表明的是一个Lambda表达式,该表达式接受一个String做为参数,返回另一个Lambda表达式。这也就是它的类型Function>所表明的意义。
目前来看,使用这种方式让代码更加复杂了,可是将它简化以后就成了下面这个样子:
final Function<String, Predicate<String>> startsWithLetter = (String letter) -> (String name) -> name.startsWith(letter);
还能够经过省略参数类型进行进一步的简化:
final Function<String, Predicate<String>> startsWithLetter = letter -> name -> name.startsWith(letter);
乍一看也许以为上面的形式太复杂,其实否则,你只是须要时间来适应这种简练的表达方式。
那么,咱们须要实现的代码就能够这样写了:
final long countFriendsStartN = friends.stream().filter(startsWithLetter.apply("N")).count(); final long countFriendsStartB = friends.stream().filter(startsWithLetter.apply("B")).count();
使用startsWithLetter.apply("N")
的结果是获得了Lambda表达式,它做为参数被传入到了filter方法中。剩下的事情,就和以前的代码同样了。
目前,已经出现了两种类型的函数式接口(Functional Interface)。它们分别是filter方法使用的Predicate和map方法使用的Function。其实从Java 8的源代码来看,它们的概念其实是至关简单的:
@FunctionalInterface
public interface Predicate<T> { boolean test(T t); // others... } @FunctionalInterface public interface Function<T, R> { R apply(T t); // others... }
Predicate能够看作是Function的一个特例,即Function表明的就是Predicate。
好比,当咱们须要打印出集合中第一个以某字母开头的元素时,最“天然”的实现以下:
public static void pickName( final List<String> names, final String startingLetter) { String foundName = null; for(String name : names) { if(name.startsWith(startingLetter)) { foundName = name; break; } } System.out.print(String.format("A name starting with %s: ", startingLetter)); if(foundName != null) { System.out.println(foundName); } else { System.out.println("No name found"); } }
虽然是最“天然”的实现方式,可是它太太太丑陋了。从将foundName设置成null开始,这段代码充斥着一些代码的“坏味道”。正由于变量被设置成了null,为了不臭名昭著的NullPointerException,咱们必须在使用它以前进行空检查。除此以外,声明了可变变量,使用了冗长的外部遍历,没有尽可能实现不可变性也是这段代码具备的问题。
然而,任务自己是很简单的。咱们只是想打印集合中第一个符合某种条件的元素而已。
此次,使用Lambda表达式来实现:
public static void pickName( final List<String> names, final String startingLetter) { final Optional<String> foundName = names.stream() .filter(name ->name.startsWith(startingLetter)) .findFirst(); System.out.println(String.format("A name starting with %s: %s", startingLetter, foundName.orElse("No name found"))); }
以上代码出现了几个新概念: 在调用filter后,调用了findFirst方法(找到符合条件的第一个元素),这个方法返回的对象类型时Optional。关于这个Optional,能够将它理解成一个可能存在,也可能不存在的结果。这样的话,就能够避免对返回结果进行空检查了。对于结果是否真的存在,可使用isPresent()方法进行判断,而get()方法用于尝试对结果的获取。当结果不存在时,咱们也可使用orElse()来指定一个替代结果,正如上面使用的那样。
另外,当结果存在时,经过使用ifPresent方法也能够运行某一段代码,运行的代码能够经过Lambda表达式声明:
foundName.ifPresent(name -> System.out.println("Hello " + name));
可是,对于使用Lambda表达式实现的pickName方法,它作的工做是否会比命令式的实现方式更多呢?由于能够发现,在命令式实现中,当咱们发现了第一个符号条件的元素以后,for循环会被当即终止。而findFirst是否也会执行类型的操做,当发现第一个符号条件的元素后,及时中断剩下的操做呢?答案是确定的,关于这一点会在后面的文章中会进行介绍。
和前面的种种操做不一样,对于集合的归约(Collection Reduction),元素与元素再也不是独立的,它们会经过某种归约操做联系在一块儿。
好比获得名字集合的总字符数,就是一种典型的求和归约。能够实现以下:
System.out.println("Total number of characters in all names: " + friends.stream() .mapToInt(name -> name.length()) .sum());
经过stream实例的mapToInt方法,咱们能够很方便地将一个字符串集合转换成一个整型数集合。而后调用sum方法获得整型数集合的和值。这里有一些实现细节,好比mapToInt方法获得的是一个Stream类型的子类型IntStream的实例,sum方法就是定义在IntStream类型之上。与IntStream相似,还有LongStream和DoubleStream类型,这些类型的存在是为了提供一些类型相关的操做,让代码调用更简洁。
好比,和sum()方法相似地,还有max(),min(),average()等一系列方法用来实现经常使用的归约。
//计算 count, min, max, sum, and average for numbers
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
IntSummaryStatistics stats = numbers
.stream()
.mapToInt((x) -> x)
.summaryStatistics();
System.out.println("List中最大的数字 : " + stats.getMax());
System.out.println("List中最小的数字 : " + stats.getMin());
System.out.println("全部数字的总和 : " + stats.getSum());
System.out.println("全部数字的平均值 : " + stats.getAverage());
可是归根到底,这些方法最终使用到的是一个叫作reduce()的方法。reduce方法的工做原理,能够这样归纳:在对一个集合中的元素按照顺序进行两两操做时,根据某种策略来获得一个结果,获得的结果将做为一个元素参与到下一次操做中,最终这个集合会被归约成为一个结果。这个结果也就是reduce方法的返回值。
所以,当咱们须要寻找并打印一个集合中最长的名字时(长度相同时,打印第一个),能够以下实现:
final Optional<String> aLongName = friends.stream() .reduce((name1, name2) -> name1.length() >= name2.length() ? name1 : name2); aLongName.ifPresent(name -> System.out.println(String.format("A longest name: %s", name)));
咱们来分析一下Lambda表达式:
(name1, name2) -> name1.length() >= name2.length() ? name1 : name2)
是否是符合咱们归纳的关于reduce方法的工做原理。
第一次执行两两操做时,name1和name2表明的是集合中的第一个和第二个元素,当第一个元素的长度大于等于第二个元素时,将第一个元素保留下来,不然保留第二个元素。 第二次执行两两操做时,name1表明的是上一次操做中被保留下来的拥有较长长度的元素,name2表明的是第三个元素。 以此类推...最后获得的结果就是集合中第一个拥有最长长度的元素了。
实际上,reduce方法接受的Lambda表达式的行为被抽象成了BinaryOperator接口:
@FunctionalInterface
public interface BinaryOperator<T> extends BiFunction<T,T,T> { // others... } @FunctionalInterface public interface BiFunction<T, U, R> { /** * Applies this function to the given arguments. * * @param t the first function argument * @param u the second function argument * @return the function result */ R apply(T t, U u); // others... }
源码也反映了BinaryOperator和另外一个函数式接口BiFunction之间的关系,当BiFunction接口中接受的三个参数类型一致时,也就成为了一个BinaryOperator接口。所以,前者其实是后者的一个特例。
另外须要注意的几点:
好比,如下代码为reduce方法传入了默认值:
final String steveOrLonger = friends.stream() .reduce("Steve", (name1, name2) -> name1.length() >= name2.length() ? name1 : name2);
在过去,咱们使用for循环来链接一个集合中的全部元素:
for(String name : friends) { System.out.print(name + ", "); } System.out.println();
上述代码的问题是,在最后一个名字后面也出现了讨人厌的逗号!为了修复这个问题:
for(int i = 0; i < friends.size() - 1; i++) { System.out.print(friends.get(i) + ", "); } if(friends.size() > 0) System.out.println(friends.get(friends.size() - 1));
嗯,结果是正确了,可是你能忍受如此丑陋的代码吗?
为了解决这个很是很是常见的问题,Java 8中终于引入了一个StringJoiner类。 能够经过调用String类型的join方法完成这个操做:
System.out.println(String.join(", ", friends));
StringJoiner其实还可以对元素的链接操做进行更多的控制。好比为每一个元素添加前缀,后缀而后再进行链接。具体的使用方法能够去参考API文档。
固然,使用reduce方法也可以完成对于集合元素的链接操做,毕竟集合元素的链接也是一种归约。只不过,正如前面看到的那样,reduce方法太过于底层了。针对这个问题,Stream类型还定义了一个collect方法用来完成一些常见的归约操做:
System.out.println(friends.stream().map(String::toUpperCase).collect(Collectors.joining(", ")));
可见collect方法并不本身完成归约操做,它会将归约操做委托给一个具体的Collector,而Collectors类型则是一个工具类,其中定义了许多常见的归约操做,好比上述的joining Collector
简单的基本操做
2、流的操做:
流的操做能够归结为几种:
一、遍历操做(map):
使用map操做能够遍历集合中的每一个对象,并对其进行操做,map以后,用.collect(Collectors.toList())会获得操做后的集合。
1.一、遍历转换为大写:
List<String> output = wordList.stream().
map(String::toUpperCase).
collect(Collectors.toList());
1.二、平方数:
List<Integer> nums = Arrays.asList(1, 2, 3, 4);
List<Integer> squareNums = nums.stream().
map(n -> n * n).
collect(Collectors.toList());
二、过滤操做(filter):
使用filter能够对象Stream中进行过滤,经过测试的元素将会留下来生成一个新的Stream
List<Person> phpProgrammers = new ArrayList<Person>();
phpProgrammers.add(new Person("Jarrod", "Pace", "PHP programmer", "male", 34, 1550));
phpProgrammers.add(new Person("Clarette", "Cicely", "PHP programmer", "female", 23, 1200));
phpProgrammers.add(new Person("Victor", "Channing", "PHP programmer", "male", 32, 1600));
//根据本身的规则判断集合中是否存在某些数据
boolean isExits = phpProgrammers.stream().anyMatch(obj -> (obj.getFirstName().equals("Quinn") && obj.getLastName().equals("Tamara")));
System.out.println(isExits+"===isExits");
//根据本身的规则判断集合中是否存在某些数据添加到新的集合并打印
List<Person> lists = phpProgrammers.parallelStream().filter(php->(php.getFirstName().equals("Quinn") && php.getLastName().equals("Tamara")))
.collect(Collectors.toList());
lists.forEach(li->System.out.println(li.getFirstName()));
2.一、获得其中不为空的String
List<String> filterLists = new ArrayList<>();
filterLists.add("");
filterLists.add("a");
filterLists.add("b");
List afterFilterLists = filterLists.stream()
.filter(s -> !s.isEmpty())
.collect(Collectors.toList());
三、循环操做(forEach):
若是只是想对流中的每一个对象进行一些自定义的操做,可使用forEach:
List<String> forEachLists = new ArrayList<>();
forEachLists.add("a");
forEachLists.add("b");
forEachLists.add("c");
forEachLists.stream().forEach(s-> System.out.println(s));
四、返回特定的结果集合(limit/skip):
limit 返回 Stream 的前面 n 个元素;skip 则是扔掉前 n 个元素:
List<String> forEachLists = new ArrayList<>();
forEachLists.add("a");
forEachLists.add("b");
forEachLists.add("c");
forEachLists.add("d");
forEachLists.add("e");
forEachLists.add("f");
List<String> limitLists = forEachLists.stream().skip(2).limit(3).collect(Collectors.toList());
注意skip与limit是有顺序关系的,好比使用skip(2)会跳过集合的前两个,返回的为c、d、e、f,而后调用limit(3)会返回前3个,因此最后返回的c,d,e
五、排序(sort/min/max/distinct):
sort能够对集合中的全部元素进行排序。max,min能够寻找出流中最大或者最小的元素,而distinct能够寻找出不重复的元素:
5.一、对一个集合进行排序:
List<Integer> sortLists = new ArrayList<>();
sortLists.add(1);
sortLists.add(4);
sortLists.add(6);
sortLists.add(3);
sortLists.add(2);
List<Integer> afterSortLists = sortLists.stream().sorted((In1,In2)->
In1-In2).collect(Collectors.toList());
5.二、获得其中长度最大的元素:
List<String> maxLists = new ArrayList<>();
maxLists.add("a");
maxLists.add("b");
maxLists.add("c");
maxLists.add("d");
maxLists.add("e");
maxLists.add("f");
maxLists.add("hahaha");
int maxLength = maxLists.stream().mapToInt(s->s.length()).max().getAsInt();
System.out.println("字符串长度最长的长度为"+maxLength);
5.三、对一个集合进行查重:
List<String> distinctList = new ArrayList<>();
distinctList.add("a");
distinctList.add("a");
distinctList.add("c");
distinctList.add("d");
List<String> afterDistinctList = distinctList.stream().distinct().collect(Collectors.toList());
其中的distinct()方法能找出stream中元素equal(),即相同的元素,并将相同的去除,上述返回即为a,c,d。
六、匹配(Match方法):
有的时候,咱们只须要判断集合中是否所有知足条件,或者判断集合中是否有知足条件的元素,这时候就可使用match方法:
allMatch:Stream 中所有元素符合传入的 predicate,返回 true
anyMatch:Stream 中只要有一个元素符合传入的 predicate,返回 true
noneMatch:Stream 中没有一个元素符合传入的 predicate,返回 true
6.一、判断集合中没有有为‘c’的元素:
List<String> matchList = new ArrayList<>();
matchList.add("a");
matchList.add("a");
matchList.add("c");
matchList.add("d");
boolean isExits = matchList.stream().anyMatch(s -> s.equals("c"));
6.二、判断集合中是否全不为空:
List<String> matchList = new ArrayList<>();
matchList.add("a");
matchList.add("");
matchList.add("a");
matchList.add("c");
matchList.add("d");
boolean isNotEmpty = matchList.stream().noneMatch(s -> s.isEmpty());
则返回的为false
List<String> list1 = new ArrayList();
list1.add("1111");
list1.add("2222");
list1.add("3333");
List<String> list2 = new ArrayList();
list2.add("3333");
list2.add("qqqq");
list2.add("bbb33");
list2.add("qnnjnj");
list2.add("qhghgh");
// 交集
List<String> intersection = list1.parallelStream().filter(item -> list2.contains(item)).collect(toList());
System.out.println("---获得交集 intersection---");
intersection.parallelStream().forEach(System.out :: println);
// 差集 (list1 - list2)
List<String> reduce1 = list1.parallelStream().filter(item -> !list2.contains(item)).collect(toList());
System.out.println("---获得差集 reduce1 (list1 - list2)---");
reduce1.parallelStream().forEach(System.out :: println);
// 差集 (list2 - list1)
List<String> reduce2 = list2.parallelStream().filter(item -> !list1.contains(item)).collect(toList());
System.out.println("---获得差集 reduce2 (list2 - list1)---");
reduce2.parallelStream().forEach(System.out :: println);
// 并集
List<String> listAll = list1.parallelStream().collect(toList());
List<String> listAll2 = list2.parallelStream().collect(toList());
listAll.addAll(listAll2);
System.out.println("---获得并集 listAll---");
listAll.parallelStream().forEach(System.out :: println);
// 去重并集
List<String> listAllDistinct = listAll.stream().distinct().collect(toList());
System.out.println("---获得去重并集 listAllDistinct---");
listAllDistinct.parallelStream().forEach(System.out :: println);
System.out.println("---原来的List1---");
list1.parallelStream().forEach(System.out :: println);
System.out.println("---原来的List2---");
list2.parallelStream().forEach(System.out :: println);
获取元素的
https://blog.csdn.net/wangmuming/article/details/72743790
在这篇文章中,咱们将向您展现如何使用java 8 Stream 对列表分组,计数,求和和排序。
Collectors
今天使用lambda表达式处理集合时,发现对return、break以及continue的使用有点迷惑,因而本身动手测试了一下,才发如今使用foreach()处理集合时不能使用break和continue这两个方法,也就是说不能按照普通的for循环遍历集合时那样根据条件来停止遍历,而若是要实如今普通for循环中的效果时,可使用return来达到,也就是说若是你在一个方法的lambda表达式中使用return时,这个方法是不会返回的,而只是执行下一次遍历,看以下的测试代码:
上述代码的输出结果是以下图所示:
能够看出return起到的做用和continue是相同的。
想知道这是为何,在Stack Overflow中找到一个答案,主要是说foreach()不是一个循环,不是设计为能够用break以及continue来停止的操做。
https://blog.csdn.net/abcwywht/article/details/77991868
https://blog.csdn.net/dm_vincent/article/details/40340291
本文基原本自这两个网站 对学习拉姆达的初学者颇有用处 想升级本身的能够好好学学