Lambda expression:a function (or a subroutine) defined, and possibly called, without being bound to an identifier。
Lambda表达式的语法由参数列表
、箭头符号->
和函数体
组成。
函数体
既可以是一个表达式,也可以是一个语句块:
表达式:表达式会被执行然后返回执行结果。
语句块:语句块中的语句会被依次执行,就像方法中的语句一样。
return语句会把控制权交给匿名方法的调用者
break和continue只能在循环中使用
如果函数体有返回值,那么函数体内部的每一条路径都必须返回值
(Type1 param1, Type2 param2, ..., TypeN paramN) -> {
statment1;
statment2;
//.............
return statmentM;
}
(param1,param2, ..., paramN) -> {
statment1;
statment2;
//.............
return statmentM;
}
param1 -> {
statment1;
statment2;
//.............
return statmentM;
}
param1 -> statment
// 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)
String[] atp = {"Rafael Nadal", "Novak Djokovic",
"Stanislas Wawrinka",
"David Ferrer","Roger Federer",
"Andy Murray","Tomas Berdych",
"Juan Martin Del Potro"};
List players = Arrays.asList(atp);
// 以前的循环方式
for (String player : players) {
System.out.print(player + "; ");
}
// 使用 lambda 表达式以及函数操作(functional operation)
players.forEach((player) -> System.out.print(player + "; ")); // 在 Java 8 中使用双冒号操作符(double colon operator) players.forEach(System.out::println);
// 使用匿名内部类
btn.setOnAction(new EventHandler() {
@Override
public void handle(ActionEvent event) {
System.out.println("Hello World!");
}
});
// 或者使用 lambda expression
btn.setOnAction(event -> System.out.println("Hello World!"));
// 1.1 使用匿名内部类根据 surname 排序 players
Arrays.sort(players, new Comparator() {
@Override
public int compare(String s1, String s2) {
return (s1.substring(s1.indexOf(" ")).compareTo(s2.substring(s2.indexOf(" "))));
}
});
// 1.2 使用 lambda expression 排序,根据 surname
Comparator sortBySurname = (String s1, String s2) ->
( s1.substring(s1.indexOf(" ")).compareTo( s2.substring(s2.indexOf(" ")) ) );
Arrays.sort(players, sortBySurname);
// 1.3 或者这样,怀疑原作者是不是想错了,括号好多...
Arrays.sort(players, (String s1, String s2) -> ( s1.substring(s1.indexOf(" ")).compareTo( s2.substring(s2.indexOf(" ")) ) ) ); // 2.1 使用匿名内部类根据 name lenght 排序 players Arrays.sort(players, new Comparator() { @Override public int compare(String s1, String s2) { return (s1.length() - s2.length()); } }) ; // 2.2 使用 lambda expression 排序,根据 name lenght Comparator<String> sortByNameLenght = (String s1, String s2) -> (s1.length() - s2.length());
Arrays.sort(players, sortByNameLenght);
// 2.3 or this
Arrays.sort(players, (String s1, String s2) -> (s1.length() - s2.length())); // 3.1 使用匿名内部类排序 players, 根据最后一个字母 Arrays.sort(players, new Comparator() { @Override public int compare(String s1, String s2) { return (s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1)); } }) ; // 3.2 使用 lambda expression 排序,根据最后一个字母 Comparator<String> sortByLastLetter = (String s1, String s2) ->
(s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1));
Arrays.sort(players, sortByLastLetter);
// 3.3 or this
Arrays.sort(players, (String s1, String s2) -> (s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1)));
前面介绍lambda表达式简化的时候,已经看过方法引用的身影了。方法引用可以在某些条件成立的情况下,更加简化lambda表达式的声明。方法引用语法格式有以下三种:
objectName::instanceMethod
ClassName::staticMethod
ClassName::instanceMethod
前两种方式类似,等同于把lambda表达式的参数直接当成instanceMethod|staticMethod的参数来调用。比如System.out::println等同于x->System.out.println(x);Math::max等同于(x, y)->Math.max(x,y)。
最后一种方式,等同于把lambda表达式的第一个参数当成instanceMethod的目标对象,其他剩余参数当成该方法的参数。比如String::toLowerCase等同于x->x.toLowerCase()。
构造器引用语法如下:ClassName::new,把lambda表达式的参数当成ClassName构造器的参数 。例如BigDecimal::new等同于x->new BigDecimal(x)。
所以使用Stream的基本步骤:
Arrays.asList(1,2,3).stream()
。Arrays.stream(new int[]{1,2,3})
。Stream.of(Object[])
, IntStream.range(int, int)
或者 Stream.iterate(Object, UnaryOperator)
,如Stream.iterate(0, n -> n * 2)
,或者generate(Supplier s)
如Stream.generate(Math::random)
。BufferedReader.lines()
从文件中获得行的流。BitSet.stream()
, Pattern.splitAsStream(java.lang.CharSequence)
, 和 JarFile.stream()
。List l = Stream.of("a","b","c","b")
.distinct()
.collect(Collectors.toList());
System.out.println(l); //[a, b, c]
Filter接受一个predicate接口类型的变量,并将所有流对象中的元素进行过滤。该操作是一个中间操作,因此它允许我们在返回结果的基础上再进行其他的流操作(forEach)。
Predicate ageFilter = (p) -> (p.getAge() > 25);
Predicate genderFilter = (p) -> ("female".equals(p.getGender()));
javaProgrammers.stream()
.filter(ageFilter)
.filter(genderFilter)
.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
List l = Stream.of('a','b','c')
.map( c -> c.hashCode())
.collect(Collectors.toList());
System.out.println(l); //[97, 98, 99]
javaProgrammers.stream()
.limit(3)
.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
sorted只是创建一个流对象排序的视图,而不会改变原来集合中元素的顺序。原来string集合中的元素顺序是没有改变的。
String[] arr = new String[]{"b_123","c+342","b#632","d_123"};
List l = Arrays.stream(arr)
.sorted((s1,s2) -> { if (s1.charAt(0) == s2.charAt(0)) return s1.substring(2).compareTo(s2.substring(2)); else return s1.charAt(0) - s2.charAt(0); }) .collect(Collectors.toList()); System.out.println(l); //[b_123, b#632, c+342, d_123]
public boolean allMatch(Predicate super T> predicate)
public boolean anyMatch(Predicate super T> predicate)
public boolean noneMatch(Predicate super T> predicate)
这一组方法用来检查流中的元素是否满足断言。
allMatch只有在所有的元素都满足断言时才返回true,否则flase,流为空时总是返回true
anyMatch只有在任意一个元素满足断言时就返回true,否则flase,
noneMatch只有在所有的元素都不满足断言时才返回true,否则flase,
System.out.println(Stream.of(1,2,3,4,5).allMatch( i -> i > 0)); //true
System.out.println(Stream.of(1,2,3,4,5).anyMatch( i -> i > 0)); //true
System.out.println(Stream.of(1,2,3,4,5).noneMatch( i -> i > 0)); //false
System.out.println(Stream.empty().allMatch( i -> i > 0)); //true
System.out.println(Stream.empty().anyMatch( i -> i > 0)); //false
System.out.println(Stream.empty().noneMatch( i -> i > 0)); //true
Count是一个终结操作,它的作用是返回一个数值,用来标识当前流对象中包含的元素数量
count方法返回流中的元素的数量。它实现为:
mapToLong(e -> 1L).sum();
<R,A> R collect(Collector super T,A,R> collector)
<R> R collect(Supplier<R> supplier, BiConsumer<R,? super T> accumulator, BiConsumer<R,R> combiner)
使用一个collector执行mutable reduction操作。辅助类Collectors提供了很多的collector,可以满足我们日常的需求,你也可以创建新的collector实现特定的需求。它是一个值得关注的类,你需要熟悉这些特定的收集器,如聚合类averagingInt、最大最小值maxBy minBy、计数counting、分组groupingBy、字符串连接joining、分区partitioningBy、汇总summarizingInt、化简reducing、转换toXXX等。
第二个提供了更底层的功能,它的逻辑类似下面的伪代码:
R result = supplier.get();
for (T element : this stream)
accumulator.accept(result, element);
return result;
例子:
List<String> asList = stringStream.collect(ArrayList::new, ArrayList::add,
ArrayList::addAll);
String concat = stringStream.collect(StringBuilder::new, StringBuilder::append,
StringBuilder::append)
.toString();
findAny()返回任意一个元素,如果流为空,返回空的Optional,对于并行流来说,它只需要返回任意一个元素即可,所以性能可能要好于findFirst(),但是有可能多次执行的时候返回的结果不一样。 findFirst()返回第一个元素,如果流为空,返回空的Optional。
forEach遍历流的每一个元素,执行指定的action。它是一个终点操作,和peek方法不同。这个方法不担保按照流的encounter order顺序执行,如果对于有序流按照它的encounter order顺序执行,你可以使用forEachOrdered方法。
Stream.of(1,2,3,4,5).forEach(System.out::println);
max返回流中的最大值,
min返回流中的最小值。
reduce是常用的一个方法,事实上很多操作都是基于它实现的。
它有几个重载方法:
pubic Optional reduce(BinaryOperator accumulator)
pubic T reduce(T identity, BinaryOperator accumulator)
pubic U reduce(U identity, BiFunctionsuper T,U> accumulator, BinaryOperator combiner)
第一个方法使用流中的第一个值作为初始值,后面两个方法则使用一个提供的初始值。
Optional total = Stream.of(1,2,3,4,5).reduce( (x, y) -> x +y); Integer total2 = Stream.of(1,2,3,4,5).reduce(0, (x, y) -> x +y); 值得注意的是accumulator应该满足结合性(associative)
将流中的元素放入到一个数组中
toArray方法将一个流转换成数组,而如果想转换成其它集合类型,西需要调用collect方法,利用Collectors.toXXX方法进行转换:
public static > Collector toCollection(Supplier collectionFactory)
public static …… toConcurrentMap(……)
public static Collector> toList()
public static …… toMap(……)
public static Collector> toSet()
Predicate是一个布尔类型的函数,该函数只有一个输入参数。Predicate接口包含了多种默认方法,用于处理复杂的逻辑动词(and, or,negate)
Predicate<String> predicate = (s) -> s.length() > 0;
predicate.test("foo"); // true
predicate.negate().test("foo"); // false
Predicate<Boolean> nonNull = Objects::nonNull;
Predicate<Boolean> isNull = Objects::isNull;
Predicate<String> isEmpty = String::isEmpty;
Predicate<String> isNotEmpty = isEmpty.negate();
Function接口接收一个参数,并返回单一的结果。默认方法可以将多个函数串在一起(compse, andThen)
Function<String, Integer> toInteger = Integer::valueOf;
Function<String, String> backToString = toInteger.andThen(String::valueOf);
backToString.apply("123"); // "123"
Supplier接口产生一个给定类型的结果。与Function不同的是,Supplier没有输入参数。
Supplier personSupplier = Person::new;
personSupplier.get(); // new Person
Consumer代表了在单一的输入参数上需要进行的操作。
Consumer greeter = (p) -> System.out.println("Hello, " + p.firstName);
greeter.accept(new Person("Luke", "Skywalker"));
Comparator接口在早期的Java版本中非常著名。Java 8 为这个接口添加了不同的默认方法。
Comparator comparator = (p1, p2) -> p1.firstName.compareTo(p2.firstName);
Person p1 = new Person("John", "Doe");
Person p2 = new Person("Alice", "Wonderland");
comparator.compare(p1, p2); // > 0
comparator.reversed().compare(p1, p2); // < 0
Java8初体验(一)lambda表达式语法
JDK 1.8新特性Lambda入门
Java Stream 详解
Java8 简明教程