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)流中所有匹配,部分匹配,徹底不匹配,返回布爾值