lambda是函数式编程(FP,functional program),在java8中引入,而C#很早以前就有了。在java中lambda表达式是'->',在C#中是‘=>’。java
杜甫说:射人先射马,擒贼先擒王。学习一个库要学习它的入口类。lambda的入口类是Stream,一看Stream中的函数就会发现Function,Predicate等lambda元素。sql
一.几个概念编程
函数式接口 Functional Interface,除了static和default类型的方法外,只有一个函数的接口。之前,接口中的一切方法都是public的,如今接口中能够包含default类型的实现方法了。java中没有函数指针的概念,C#中有delegate委托至关于函数指针,但java也是有办法的,用一个类,类里面有一个函数,这个类就至关于函数指针。这么整实现简单,理解简单,可是代码比较冗长。app
谓词 Predicate, 简单来讲,谓词就是条件。正规来讲,谓词就是一个函数boolean f(x1,x2...),表示变量x1,x2...是否知足条件f。在java中谓词的定义就是一个函数式接口。框架
函数(映射) Function,将一种类型的对象映射为另外一种或同种类型的对象,它就是一个函数ObjectA f(ObjectB)。在java中映射的定义也是一个函数式接口。less
@FunctionalInterface public interface Function<T, R> { R apply(T t); default <V> Function<V, R> compose(Function<? super V, ? extends T> before) { Objects.requireNonNull(before); return (V v) -> apply(before.apply(v)); } default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) { Objects.requireNonNull(after); return (T t) -> after.apply(apply(t)); } static <T> Function<T, T> identity() { return t -> t; } }
可见,除了apply()函数之外,其他default方法在外部都是不可见的。因此定义函数式接口的时候须要把其余函数声明称static或者default类型的。dom
Optional这个值表示一个对象,这个对象可能为空也可能不为空,对于它能够产生许多行为:ide
若是它是null,该怎么作orElse()和orElseGet()函数式编程
若是不为null,该怎么作ifPresent()函数
判断是否为null,isPresent()
这个类看上去十分鸡肋,但用处十分普遍
package aaa; import java.util.NoSuchElementException; import java.util.Optional; public class OptionalDemo { public static void main(String[] args) { //建立Optional实例,也能够经过方法返回值获得。 Optional<String> name = Optional.of("Sanaulla"); //建立没有值的Optional实例,例如值为'null' Optional<Object> empty = Optional.ofNullable(null); //isPresent方法用来检查Optional实例是否有值。 if (name.isPresent()) { //调用get()返回Optional值。 System.out.println(name.get()); } try { //在Optional实例上调用get()抛出NoSuchElementException。 System.out.println(empty.get()); } catch (NoSuchElementException ex) { System.out.println(ex.getMessage()); } //ifPresent方法接受lambda表达式参数。 //若是Optional值不为空,lambda表达式会处理并在其上执行操做。 name.ifPresent((value) -> { System.out.println("The length of the value is: " + value.length()); }); //若是有值orElse方法会返回Optional实例,不然返回传入的错误信息。 System.out.println(empty.orElse("There is no value present!")); System.out.println(name.orElse("There is some value!")); //orElseGet与orElse相似,区别在于传入的默认值。 //orElseGet接受lambda表达式生成默认值。 System.out.println(empty.orElseGet(() -> "Default Value")); System.out.println(name.orElseGet(() -> "Default Value")); try { //orElseThrow与orElse方法相似,区别在于返回值。 //orElseThrow抛出由传入的lambda表达式/方法生成异常。 empty.orElseThrow(Exception::new); } catch (Throwable ex) { System.out.println(ex.getMessage()); } //map方法经过传入的lambda表达式修改Optonal实例默认值。 //lambda表达式返回值会包装为Optional实例。 Optional<String> upperName = name.map((value) -> value.toUpperCase()); System.out.println(upperName.orElse("No value found")); //flatMap与map(Funtion)很是类似,区别在于lambda表达式的返回值。 //map方法的lambda表达式返回值能够是任何类型,可是返回值会包装成Optional实例。 //可是flatMap方法的lambda返回值老是Optional类型。 upperName = name.flatMap((value) -> Optional.of(value.toUpperCase())); System.out.println(upperName.orElse("No value found")); //filter方法检查Optiona值是否知足给定条件。 //若是知足返回Optional实例值,不然返回空Optional。 Optional<String> longName = name.filter((value) -> value.length() > 6); System.out.println(longName.orElse("The name is less than 6 characters")); //另外一个示例,Optional值不知足给定条件。 Optional<String> anotherName = Optional.of("Sana"); Optional<String> shortName = anotherName.filter((value) -> value.length() > 6); System.out.println(shortName.orElse("The name is less than 6 characters")); } }
二.lambda表达式
有两个做用
* 做为函数指针
* 替代匿名内部类,替代函数式接口(FunctionalInterface)
lambda不是语法糖,它在内部实现上也跟匿名内部类不一样,匿名内部类须要进行类文件加载,而lambda表达式不用,因此lambda表达式效率比匿名内部类高。
三种用法
用'(x1,x2,x3)'表示传入参数
若是无参写做’()‘
参数类型能够指明,也能够不指明,java会根据后半部分函数形参自动推断出来。
List<String> a = Arrays.asList("we i di ao is great".split(" ")); a.forEach((s) -> System.out.println(s));// 表达式 a.forEach((String s) -> { System.out.println(s); });// 语句块 a.forEach(System.out::println);// 函数
方法引用
object::fun()
className::fun()静态方法引用
className::new 构造函数引用
public class LambdaIntro { // functional interface 函数式接口 public static interface ItemWithIndexVisitor<E> { public void visit(E item, int index); } public static <E> void eachWithIndex(List<E> list, ItemWithIndexVisitor<E> visitor) { for (int i = 0; i < list.size(); i++) { visitor.visit(list.get(i), i); } } // 一个普通函数,用做函数指针 public static <E> void printItem(E value, int index) { String output = String.format("%d -> %s", index, value.toString()); System.out.println(output); } public static void main(String[] args) { List<String> list = Arrays.asList("A", "B", "C"); // 第一种方式 eachWithIndex(list, (value, index) -> { String output = String.format("%d -> %s", index, value); System.out.println(output); }); // 第二种方式 eachWithIndex(list, LambdaIntro::printItem); } }
三.使用Stream
建立Stream的两种方式
* Stream接口的工厂方法
* 集合框架的stream()函数
首先来了解使用Stream接口来建立Stream,能够建立三种流:普通枚举流,产生器,迭代器。
//of:经过枚举方式建立流 Stream<Integer> one = Stream.of(1, 2, 3); //流是能够拼接的,从而产生新流 Stream<Integer> two = Stream.concat(one, Stream.of(4, 5, 6)); two.forEach(System.out::println); //逐个加入,那就用Builder构建器来实现 Builder<Integer> builder = Stream.builder(); Stream<Integer> three = builder.add(3).add(4).build(); three.forEach((s) -> System.out.println(s)); //产生器流generator Random random = new Random(); Stream<Integer> four = Stream.generate(() -> random.nextInt()); four.limit(10).forEach(System.out::println); //迭代器iterator,UnaryOperator一元运算符能够经过lambda表达式来建立 Stream<Integer>five=Stream.iterate(2, new UnaryOperator<Integer>() { @Override public Integer apply(Integer t) { return t = (t * 5 + 7) % 13; } }); five.limit(10).forEach(System.out::println);
注意产生器generator和迭代器iterator是无限输出的,能够用limit来约束之。
集合框架都继承了Collection接口,而Collection接口就有一个stream()函数。因此剩下的任务就是如何利用流的强大特性来写出优雅的代码来。
要想深入的了解Stream的一些函数,那就先不要使用lambda表达式,一旦了解它的普通实现,很容易改写成lambda的形式。
流Stream中的函数明显分为两类,一类返回值仍是Stream,能够继续用流来处理,另外一类返回值不是Stream,不能再用流中函数处理了。
Stream filter(Predicate)删除掉流中不知足条件的元素并返回新的Stream
Stream map(Function)映射,把流中的元素映射一下变成一个新流,还有mapToInt(),mapToLong(),mapToDouble()等函数,它们终究仍是映射,只是映射结果更单一。map是一对一映射,flatMap是一对多映射。把一个元素映射成多个元素
Arrays.asList(1, 2, 3).stream() .flatMap(new Function<Integer, Stream<Integer>>() { @Override public Stream<Integer> apply(Integer t) { return Arrays.asList(t, t + 10, t + 100).stream(); } }).forEach(System.out::println);
输出为1 11 101 2 12 102 3 13 103 中间我省略了换行符
distinct()去除流中重复元素
sorted()和sorted(Comparator cmp)对流中元素排序
peek()弹出一个元素
Stream.of("one", "two", "three", "four").filter(e -> e.length() > 3) .peek(e -> System.out.println("Filtered value: " + e)) .map(String::toUpperCase) .peek(e -> System.out.println("Mapped value: " + e)) .collect(Collectors.toList());
执行结果
Filtered value: three
Mapped value: THREE
Filtered value: four
Mapped value: FOUR
limit(int cnt)只返回cnt个元素,skip(int cnt)跳过cnt个元素。
forEach(Consumer consumer)对于每个元素都执行某种操做
reduce()将多个值映射为一个值,实现多对一映射
Stream.of("one", "two", "three", "four") .reduce(new BinaryOperator<String>() { @Override public String apply(String t, String u) { System.out.println(t+":"+u); return t + "," + u; } }).ifPresent(System.out::println);
输出为
one:two
one,two:three
one,two,three:four
one,two,three,four
可见,apply(t,u)函数中的t表示当前总量,u表示当前元素。
reduce(T identity,BinaryOperator<T>f)表示带初始值的reduce,好比求和函数,若是identity=9,表示一开始sum=9,此函数返回具体的对象。
String s = Stream.of("one", "two", "three", "four").reduce("baga", new BinaryOperator<String>() { @Override public String apply(String t, String u) { return t +","+ u; } }); System.out.println(s);
输出:
baga,one,two,three,four
collect()
Map<String, Map<String, List<Person>>> peopleByStateAndCity = personStream.collect(Collectors.groupingBy(Person::getState, Collectors.groupingBy(Person::getCity)));
String s=Stream.of("one", "two", "three", "four") .collect(Collectors.joining(",")); System.out.println(s);
输出one,two,three,four
Collectors包含许多有用的静态方法
汇集函数min(),max(),count()很像sql中的汇集函数
匹配函数allMatch(Predicate p),anyMatch(Predicate p),noneMath(Predicate p)流中所有匹配,部分匹配,彻底不匹配,返回布尔值