咱們關心的是如何寫出好代碼,而不是符合函數編程風格的代碼。 @pdaihtml
最全的Java後端知識體系 https://www.pdai.tech, 天天更新中...。java
在Java世界裏面,面向對象仍是主流思想,對於習慣了面向對象編程的開發者來講,抽象的概念並不陌生。面向對象編程是對數據進行抽象,而函數式編程是對行爲進行抽象。現實世界中,數據和行爲並存,程序也是如此,所以這兩種編程方式咱們都得學。git
這種新的抽象方式還有其餘好處。不少人不老是在編寫性能優先的代碼,對於這些人來講,函數式編程帶來的好處尤其明顯。程序員能編寫出更容易閱讀的代碼——這種代碼更多地表達了業務邏輯,而不是從機制上如何實現。易讀的代碼也易於維護、更可靠、更不容易出錯。程序員
在寫回調函數和事件處理器時,程序員沒必要再糾纏於匿名內部類的冗繁和可讀性,函數式編程讓事件處理系統變得更加簡單。能將函數方便地傳遞也讓編寫惰性代碼變得容易,只有在真正須要的時候,才初始化變量的值。github
面向對象編程是對數據進行抽象;函數式編程是對行爲進行抽象。web
核心思想:使用不可變值和函數,函數對一個值進行處理,映射成另外一個值。正則表達式
對核心類庫的改進主要包括集合類的API和新引入的流Stream。流使程序員能夠站在更高的抽象層次上對集合進行操做。express
lambda表達式僅能放入以下代碼:預約義使用了 @Functional 註釋的函數式接口,自帶一個抽象函數的方法,或者SAM(Single Abstract Method 單個抽象方法)類型。這些稱爲lambda表達式的目標類型,能夠用做返回類型,或lambda目標代碼的參數。例如,若一個方法接收Runnable、Comparable或者 Callable 接口,都有單個抽象方法,能夠傳入lambda表達式。相似的,若是一個方法接受聲明於 java.util.function 包內的接口,例如 Predicate、Function、Consumer 或 Supplier,那麼能夠向其傳lambda表達式。編程
方法引用
,僅當該方法不修改lambda表達式提供的參數。本例中的lambda表達式能夠換爲方法引用,由於這僅是一個參數相同的簡單方法調用。list.forEach(n -> System.out.println(n)); list.forEach(System.out::println); // 使用方法引用
然而,若對參數有任何修改,則不能使用方法引用,而需鍵入完整地lambda表達式,以下所示:後端
list.forEach((String s) -> System.out.println("*" + s + "*"));
事實上,能夠省略這裏的lambda參數的類型聲明,編譯器能夠從列表的類屬性推測出來。
lambda內部可使用靜態、非靜態和局部變量,這稱爲lambda內的變量捕獲。
Lambda表達式在Java中又稱爲閉包或匿名函數,因此若是有同事把它叫閉包的時候,不用驚訝。
private static java.lang.Object lambda$0(java.lang.String);
List<Integer> primes = Arrays.asList(new Integer[]{2, 3,5,7}); int factor = 2; primes.forEach(element -> { factor++; });
Compile time error : "local variables referenced from a lambda expression must be final or effectively final"
另外,只是訪問它而不做修改是能夠的,以下所示:
List<Integer> primes = Arrays.asList(new Integer[]{2, 3,5,7}); int factor = 2; primes.forEach(element -> { System.out.println(factor*element); });
lists.stream().filter(f -> f.getName().equals("p1"))
如上示例,這行代碼並未作什麼實際性的工做,filter只是描述了Stream,沒有產生新的集合。
若是是多個條件組合,能夠經過代碼塊{}
List<Person> list2 = lists.stream().filter(f -> f.getName().equals("p1")).collect(Collectors.toList());
如上示例,collect最終會從Stream產生新值,擁有終止操做。
理想方式是造成一個惰性求值的鏈,最後用一個及早求值的操做返回想要的結果。與建造者模式類似,建造者模式先是使用一系列操做設置屬性和配置,最後調用build方法,建立對象。
每一個Stream都有兩種模式:順序執行和並行執行。
順序流:
List <Person> people = list.getStream.collect(Collectors.toList());
並行流:
List <Person> people = list.getStream.parallel().collect(Collectors.toList());
顧名思義,當使用順序方式去遍歷時,每一個item讀完後再讀下一個item。而使用並行去遍歷時,數組會被分紅多個段,其中每個都在不一樣的線程中處理,而後將結果一塊兒輸出。
List originalList = someData; split1 = originalList(0, mid);//將數據分小部分 split2 = originalList(mid,end); new Runnable(split1.process());//小部分執行操做 new Runnable(split2.process()); List revisedList = split1 + split2;//將結果合併
你們對hadoop有稍微瞭解就知道,裏面的 MapReduce 自己就是用於並行處理大數據集的軟件框架,其 處理大數據的核心思想就是大而化小,分配到不一樣機器去運行map,最終經過reduce將全部機器的結果結合起來獲得一個最終結果,與MapReduce不一樣,Stream則是利用多核技術可將大數據經過多核並行處理,而MapReduce則能夠分佈式的。
若是是多核機器,理論上並行流則會比順序流快上一倍,下面是測試代碼
long t0 = System.nanoTime(); //初始化一個範圍100萬整數流,求能被2整除的數字,toArray()是終點方法 int a[]=IntStream.range(0, 1_000_000).filter(p -> p % 2==0).toArray(); long t1 = System.nanoTime(); //和上面功能同樣,這裏是用並行流來計算 int b[]=IntStream.range(0, 1_000_000).parallel().filter(p -> p % 2==0).toArray(); long t2 = System.nanoTime(); //我本機的結果是serial: 0.06s, parallel 0.02s,證實並行流確實比順序流快 System.out.printf("serial: %.2fs, parallel %.2fs%n", (t1 - t0) * 1e-9, (t2 - t1) * 1e-9);
stream()
, parallelStream()
filter()
findAny()
findFirst()
sort
forEach
voidmap(), reduce()
flatMap()
- 將多個Stream鏈接成一個Streamcollect(Collectors.toList())
distinct
, limit
count
min
, max
, summaryStatistics
看下全部API:
new Thread( () -> System.out.println("In Java8, Lambda expression rocks !!") ).start(); // 用法 (params) -> expression (params) -> statement (params) -> { statements }
// forEach List features = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API"); features.forEach(n -> System.out.println(n)); // 使用Java 8的方法引用更方便,方法引用由::雙冒號操做符標示, features.forEach(System.out::println);
構造引用
// Supplier<Student> s = () -> new Student(); Supplier<Student> s = Student::new;
對象::實例方法 Lambda表達式的(形參列表)與實例方法的(實參列表)類型,個數是對應
// set.forEach(t -> System.out.println(t)); set.forEach(System.out::println);
類名::靜態方法
// Stream<Double> stream = Stream.generate(() -> Math.random()); Stream<Double> stream = Stream.generate(Math::random);
類名::實例方法
// TreeSet<String> set = new TreeSet<>((s1,s2) -> s1.compareTo(s2)); /* 這裏若是使用第一句話,編譯器會有提示:Can be replaced with Comparator.naturalOrder,這句話告訴咱們 String已經重寫了compareTo()方法,在這裏寫是畫蛇添足,這裏爲何這麼寫,是由於爲了體現下面 這句編譯器的提示:Lambda can be replaced with method reference。好了,下面的這句就是改寫成方法引用以後: */ TreeSet<String> set = new TreeSet<>(String::compareTo);
常規用法
public static void main(args[]){ List languages = Arrays.asList("Java", "Scala", "C++", "Haskell", "Lisp"); System.out.println("Languages which starts with J :"); filter(languages, (str)->str.startsWith("J")); System.out.println("Languages which ends with a "); filter(languages, (str)->str.endsWith("a")); System.out.println("Print all languages :"); filter(languages, (str)->true); System.out.println("Print no language : "); filter(languages, (str)->false); System.out.println("Print language whose length greater than 4:"); filter(languages, (str)->str.length() > 4); } public static void filter(List names, Predicate condition) { names.stream().filter((name) -> (condition.test(name))).forEach((name) -> { System.out.println(name + " "); }); }
多個Predicate組合filter
// 能夠用and()、or()和xor()邏輯函數來合併Predicate, // 例如要找到全部以J開始,長度爲四個字母的名字,你能夠合併兩個Predicate並傳入 Predicate<String> startsWithJ = (n) -> n.startsWith("J"); Predicate<String> fourLetterLong = (n) -> n.length() == 4; names.stream() .filter(startsWithJ.and(fourLetterLong)) .forEach((n) -> System.out.print("nName, which starts with 'J' and four letter long is : " + n));
map將集合類(例如列表)元素進行轉換的。還有一個 reduce() 函數能夠將全部值合併成一個
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500); double bill = costBeforeTax.stream().map((cost) -> cost + .12*cost).reduce((sum, cost) -> sum + cost).get(); System.out.println("Total : " + bill);
// 將字符串換成大寫並用逗號連接起來 List<String> G7 = Arrays.asList("USA", "Japan", "France", "Germany", "Italy", "U.K.","Canada"); String G7Countries = G7.stream().map(x -> x.toUpperCase()).collect(Collectors.joining(", ")); System.out.println(G7Countries);
將多個Stream鏈接成一個Stream
List<Integer> result= Stream.of(Arrays.asList(1,3),Arrays.asList(5,6)).flatMap(a->a.stream()).collect(Collectors.toList());
結果: [1, 3, 5, 6]
去重
List<LikeDO> likeDOs=new ArrayList<LikeDO>(); List<Long> likeTidList = likeDOs.stream().map(LikeDO::getTid) .distinct().collect(Collectors.toList());
計總數
int countOfAdult=persons.stream() .filter(p -> p.getAge() > 18) .map(person -> new Adult(person)) .count();
boolean anyStartsWithA = stringCollection .stream() .anyMatch((s) -> s.startsWith("a")); System.out.println(anyStartsWithA); // true boolean allStartsWithA = stringCollection .stream() .allMatch((s) -> s.startsWith("a")); System.out.println(allStartsWithA); // false boolean noneStartsWithZ = stringCollection .stream() .noneMatch((s) -> s.startsWith("z")); System.out.println(noneStartsWithZ); // true
最小值,最大值
List<Person> lists = new ArrayList<Person>(); lists.add(new Person(1L, "p1")); lists.add(new Person(2L, "p2")); lists.add(new Person(3L, "p3")); lists.add(new Person(4L, "p4")); Person a = lists.stream().max(Comparator.comparing(t -> t.getId())).get(); System.out.println(a.getId());
若是比較器涉及多個條件,比較複雜,能夠定製
Person a = lists.stream().min(new Comparator<Person>() { @Override public int compare(Person o1, Person o2) { if (o1.getId() > o2.getId()) return -1; if (o1.getId() < o2.getId()) return 1; return 0; } }).get();
summaryStatistics
//獲取數字的個數、最小值、最大值、總和以及平均值 List<Integer> primes = Arrays.asList(2, 3, 5, 7, 11, 13, 17, 19, 23, 29); IntSummaryStatistics stats = primes.stream().mapToInt((x) -> x).summaryStatistics(); System.out.println("Highest prime number in List : " + stats.getMax()); System.out.println("Lowest prime number in List : " + stats.getMin()); System.out.println("Sum of all prime numbers : " + stats.getSum()); System.out.println("Average of all prime numbers : " + stats.getAverage());
可使用peek方法,peek方法可只包含一個空的方法體,只要能設置斷點便可,但有些IDE不容許空,能夠以下文示例,簡單寫一個打印邏輯。
注意,調試完後要刪掉。
List<Person> lists = new ArrayList<Person>(); lists.add(new Person(1L, "p1")); lists.add(new Person(2L, "p2")); lists.add(new Person(3L, "p3")); lists.add(new Person(4L, "p4")); System.out.println(lists); List<Person> list2 = lists.stream() .filter(f -> f.getName().startsWith("p")) .peek(t -> { System.out.println(t.getName()); }) .collect(Collectors.toList()); System.out.println(list2);
/** * An informative annotation type used to indicate that an interface * type declaration is intended to be a <i>functional interface</i> as * defined by the Java Language Specification. * * Conceptually, a functional interface has exactly one abstract * method. Since {@linkplain java.lang.reflect.Method#isDefault() * default methods} have an implementation, they are not abstract. If * an interface declares an abstract method overriding one of the * public methods of {@code java.lang.Object}, that also does * <em>not</em> count toward the interface's abstract method count * since any implementation of the interface will have an * implementation from {@code java.lang.Object} or elsewhere. * * <p>Note that instances of functional interfaces can be created with * lambda expressions, method references, or constructor references. * * <p>If a type is annotated with this annotation type, compilers are * required to generate an error message unless: * * <ul> * <li> The type is an interface type and not an annotation type, enum, or class. * <li> The annotated type satisfies the requirements of a functional interface. * </ul> * * <p>However, the compiler will treat any interface meeting the * definition of a functional interface as a functional interface * regardless of whether or not a {@code FunctionalInterface} * annotation is present on the interface declaration. * * @jls 4.3.2. The Class Object * @jls 9.8 Functional Interfaces * @jls 9.4.3 Interface Method Body * @since 1.8 */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface FunctionalInterface{}
interface作註解的註解類型,被定義成java語言規範
第二若是聲明的方法和java.lang.Object中的某個方法同樣,它能夠不當作未實現的方法,不違背這個原則:一個被它註解的接口只能有一個抽象方法, 好比:
java public interface Comparator<T> { int compare(T o1, T o2); boolean equals(Object obj); }
編譯器會自動把知足function interface要求的接口自動識別爲function interface,因此你纔不須要對上面示例中的 ITest接口增長@FunctionInterface註解。
@FunctionalInterface public interface IMyInterface { void study(); } package com.isea.java; public class TestIMyInterface { public static void main(String[] args) { IMyInterface iMyInterface = () -> System.out.println("I like study"); iMyInterface.study(); } }
消費型接口:Consumer< T> void accept(T t)有參數,無返回值的抽象方法;
好比:map.forEach(BiConsumer<A, T>)
Consumer<Person> greeter = (p) -> System.out.println("Hello, " + p.firstName); greeter.accept(new Person("Luke", "Skywalker"));
供給型接口:Supplier < T> T get() 無參有返回值的抽象方法;
以stream().collect(Collector<? super T, A, R> collector)爲例:
好比:
Supplier<Person> personSupplier = Person::new; personSupplier.get(); // new Person
再如:
// 調用方法 <R, A> R collect(Collector<? super T, A, R> collector) // Collectors.toSet public static <T> Collector<T, ?, Set<T>> toSet() { return new CollectorImpl<>((Supplier<Set<T>>) HashSet::new, Set::add, (left, right) -> { left.addAll(right); return left; }, CH_UNORDERED_ID); } // CollectorImpl private final Supplier<A> supplier; private final BiConsumer<A, T> accumulator; private final BinaryOperator<A> combiner; private final Function<A, R> finisher; private final Set<Characteristics> characteristics; CollectorImpl(Supplier<A> supplier, BiConsumer<A, T> accumulator, BinaryOperator<A> combiner, Function<A,R> finisher, Set<Characteristics> characteristics) { this.supplier = supplier; this.accumulator = accumulator; this.combiner = combiner; this.finisher = finisher; this.characteristics = characteristics; } CollectorImpl(Supplier<A> supplier, BiConsumer<A, T> accumulator, BinaryOperator<A> combiner, Set<Characteristics> characteristics) { this(supplier, accumulator, combiner, castingIdentity(), characteristics); } // collect()方法實現 public final <R, A> R collect(Collector<? super P_OUT, A, R> collector) { A container; if (isParallel() && (collector.characteristics().contains(Collector.Characteristics.CONCURRENT)) && (!isOrdered() || collector.characteristics().contains(Collector.Characteristics.UNORDERED))) { container = collector.supplier().get(); BiConsumer<A, ? super P_OUT> accumulator = collector.accumulator(); forEach(u -> accumulator.accept(container, u)); } else { container = evaluate(ReduceOps.makeRef(collector)); } return collector.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH) ? (R) container : collector.finisher().apply(container); }
判定型接口: Predicate<T> boolean test(T t):有參,可是返回值類型是固定的boolean
好比:steam().filter()中參數就是Predicate
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<T,R> R apply(T t)有參有返回值的抽象方法;
好比: steam().map() 中參數就是Function<? super T, ? extends R>;reduce()中參數BinaryOperator<T> (ps: BinaryOperator<T> extends BiFunction<T,T,T>)
Function<String, Integer> toInteger = Integer::valueOf; Function<String, String> backToString = toInteger.andThen(String::valueOf); backToString.apply("123"); // "123"
javaProgrammers.stream() .filter((p) -> (p.getAge() > 25)) .filter((p) -> ("female".equals(p.getGender()))) .sorted((p, p2) -> (p.getFirstName().compareTo(p2.getFirstName()))) .limit(3) //.forEach(e -> e.setSalary(e.getSalary() / 100 * 5 + e.getSalary()))//漲工資 .forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
Person person = javaProgrammers .stream() .max((p, p2) -> (p.getSalary() - p2.getSalary())) .get()
TreeSet<String> javaDevLastName = javaProgrammers .stream() .map(Person::getLastName) .collect(toCollection(TreeSet::new))
int totalSalary = javaProgrammers .parallelStream() .mapToInt(p -> p.getSalary()) .sum();
public static void main(String[] args) { List<Person> personList = getTestList(); personList.sort(Comparator.comparing(Person::getName, String.CASE_INSENSITIVE_ORDER) .thenComparing(Person::getGid, (a, b) -> b.compareTo(a)) .thenComparingInt(Person::getAge)); personList.stream().forEach(System.out::println); } public static List<Person> getTestList() { return Lists.newArrayList(new Person("dai", "301", 10), new Person("dai", "303", 10), new Person("dai", "303", 8), new Person("dai", "303", 6), new Person("dai", "303", 11), new Person("dai", "302", 9), new Person("zhang", "302", 9), new Person("zhang", "301", 9), new Person("Li", "301", 8)); } // 輸出結果 // Person [name=dai, gid=303, age=6] // Person [name=dai, gid=303, age=8] // Person [name=dai, gid=303, age=10] // Person [name=dai, gid=303, age=11] // Person [name=dai, gid=302, age=9] // Person [name=dai, gid=301, age=10] // Person [name=Li, gid=301, age=8] // Person [name=zhang, gid=302, age=9] // Person [name=zhang, gid=301, age=9]
兩個新的方法可在字符串類上使用:join和chars。第一個方法使用指定的分隔符,將任何數量的字符串鏈接爲一個字符串。
String.join(":", "foobar", "foo", "bar"); // => foobar:foo:bar
第二個方法chars從字符串全部字符建立數據流,因此你能夠在這些字符上使用流式操做。
"foobar:foo:bar" .chars() .distinct() .mapToObj(c -> String.valueOf((char)c)) .sorted() .collect(Collectors.joining()); // => :abfor
不只僅是字符串,正則表達式模式串也能受益於數據流。咱們能夠分割任何模式串,並建立數據流來處理它們,而不是將字符串分割爲單個字符的數據流,像下面這樣:
Pattern.compile(":") .splitAsStream("foobar:foo:bar") .filter(s -> s.contains("bar")) .sorted() .collect(Collectors.joining(":")); // => bar:foobar
此外,正則模式串能夠轉換爲謂詞。這些謂詞能夠像下面那樣用於過濾字符串流:
Pattern pattern = Pattern.compile(".*@gmail\\.com"); Stream.of("bob@gmail.com", "alice@hotmail.com") .filter(pattern.asPredicate()) .count(); // => 1
上面的模式串接受任何以@gmail.com結尾的字符串,而且以後用做Java8的Predicate來過濾電子郵件地址流。
public class TestLocalCache { private static ConcurrentHashMap<Integer, Long> cache = new ConcurrentHashMap<>(); static long fibonacci(int i) { if (i == 0) return i; if (i == 1) return 1; return cache.computeIfAbsent(i, (key) -> { System.out.println("Slow calculation of " + key); return fibonacci(i - 2) + fibonacci(i - 1); }); } public static void main(String[] args) { // warm up for (int i = 0; i < 101; i++) System.out.println( "f(" + i + ") = " + fibonacci(i)); // read -> cal long current = System.currentTimeMillis(); System.out.println(fibonacci(100)); System.out.println(System.currentTimeMillis()-current); } }
List<LikeDO> likeDOs=new ArrayList<LikeDO>(); List<Long> likeTidList = likeDOs.stream().map(LikeDO::getTid) .distinct().collect(Collectors.toList());
Map<String, StkProduct> newStockName2Product = Maps.newConcurrentMap(); stockProducts.stream().filter(stkProduct -> stkProduct.enabled).forEach(stkProduct -> { String newName = BCConvert.bj2qj(StringUtils.replace(stkProduct.name, " ", "")); newStockName2Product.put(newName, stkProduct); });
Set<String> qjStockNames; qjStockNames.stream().filter(name -> !acAutomaton.getKey2link().containsKey(name)).forEach(name -> { String value = ""; StkProduct stkProduct = stockNameQj2Product.get(name); if (stkProduct != null) { value = stkProduct.name; } acAutomaton.getKey2link().put(name, value); });
List<ImageModel> imageModelList = null; Map<Long, String> imagesMap = null; imagesMap = imageModelList.stream().collect(Collectors.toMap(ImageModel::getAid, o -> IMAGE_ADDRESS_PREFIX + o.getUrl())); Map<String, String> kvMap = postDetailCacheList.stream().collect(Collectors.toMap((detailCache) -> getBbsSimplePostKey(detailCache.getTid()), JSON::toJSONString)); Map<Long, Long> pidToTid; List<String> pidKeyList = pidToTid.entrySet().stream().map((o) -> getKeyBbsReplyPid(o.getValue(), o.getKey())).collect(Collectors.toList());
List<AdDO> adDOList; adDOList.stream().map(adDo -> convertAdModel(adDo)) .collect(Collectors.toList());
List<String> phones=new ArrayList<String>(); phones.add("a"); phones.add("b"); phones.add("a"); phones.add("a"); phones.add("c"); phones.add("b"); Map<String, List<String>> phoneClassify = phones.stream().collect(Collectors.groupingBy(item -> item)); System.out.println(phoneClassify); 返回結果: {a=[a, a, a], b=[b, b], c=[c]}
最全的Java後端知識體系 https://www.pdai.tech, 天天更新中...。