方法引用: method referencejava
方法引用其實是Lambda表達式的一種語法糖sql
咱們能夠將方法引用看做是一個「函數指針」,function pointer編程
public class StudentTest { public static void main(String[] args) { Student student = new Student("zhangsan",10); Student student1 = new Student("lisi",40); Student student2 = new Student("wangwu",30); Student student3 = new Student("zhaoliu",550); List<Student> list = Arrays.asList(student, student2, student3, student1); // list.forEach(item -> System.out.println(item.getName())); //1. 類名 :: 靜態方法 // list.sort((studentpar1,studentpar2) -> Student.getStudentByScore(studentpar1,studentpar2)); list.sort(Student::getStudentByScore); list.forEach(item -> System.out.println(item.getScore())); System.out.println(" - - - - - - - -- "); // 2. 引用名(對象名)::實例方法名 StudentMethod studentMethod = new StudentMethod(); list.sort(studentMethod::getStudentBySource); list.forEach(item -> System.out.println(item.getScore())); System.out.println(" - - - -- -- "); // 3. 類名:: 實例方法名 // 這個地方調用的方法只有一個參數,爲何還能正常調用呢? 由於調用比較時,第一個對象來調用getStudentByScore1. 第二個對象來當作參數 list.sort(Student::getStudentByScore1); list.forEach(item -> System.out.println(item.getScore())); System.out.println("- - - - - - - -"); // 原生的sort 來舉個例子 List<String> list1 = Arrays.asList("da", "era", "a"); // Collections.sort(list1,(city1,city2) -> city1.compareToIgnoreCase(city2)); list1.sort(String::compareToIgnoreCase); list1.forEach(System.out::println); System.out.println("- - - - - - -- "); //4. 構造方法引用 StudentTest studentTest = new StudentTest(); System.out.println(studentTest.getString(String::new)); } public String getString(Supplier<String> supplier) { return supplier.get()+"hello"; } }
defaute methodapi
默認方法是指實現此接口時,默認方法已經被默認實現。數組
引入默認方法最重要的做用就是Java要保證向後兼容。多線程
情景一: 一個類,實現了兩個接口。兩個接口中有一個相同名字的默認方法。此時會報錯,須要從寫這個重名的方法框架
情景二: 約定:實現類的優先級比接口的優先級要高。 一個類,實現一個接口,繼承一個實現類。接口和實現類中有一個同名的方法,此時,此類會使用實現類中的方法。dom
Collection提供了新的stream()方法。函數式編程
流不存儲值,經過管道的方式獲取值。函數
本質是函數式的,對流的操做會生成一個結果,不過並不會修改底層的數據源,集合能夠做爲流的底層數據源。
延遲查找,不少流操做(過濾、映射、排序等)等能夠延遲實現。
經過流的方式能夠更好的操做集合。使用函數式編程更爲流程。與lambda表達式搭配使用。
使用鏈式的調用方式sunc as : stream.xxx().yyy().zzz().count(); 沒有count的時候前邊的三個方法不會被調用。後續會進行舉例。
掌握流經常使用的api,瞭解底層。
流支持並行化,能夠多線程操做。迭代器不支持並行化。
public static void main(String[] args) { IntStream.of(1,2,4,5,6).forEach(System.out::println); IntStream.range(3, 8).forEach(System.out::println); IntStream.rangeClosed(3, 8).forEach(System.out::println); }
public static void main(String[] args) { List<Integer> list = Arrays.asList(1, 2, 3, 4, 5); System.out.println(list.stream().map(i -> i*2).reduce(0,Integer::sum)); }
函數式編程和傳統面向對象編程根本上有什麼不一樣?
傳統面向對象編程傳遞的是數據。函數式編程經過方法傳遞的是一種行爲,行爲指導了函數的處理,根據行爲對數據進行加工。
public static void main(String[] args) { Stream<String> stream = Stream.of("hello", "world", "hello world"); // String[] stringArray = stream.toArray(length -> new String[length]); //替換成方法引用的方式 --> 構造方法引用. String[] stringArray = stream.toArray(String[]::new); Arrays.asList(stringArray).forEach(System.out::println); System.out.println("- - - - - - - - - - -"); //將流轉換成list, 有現成的封裝好的方法 Stream<String> stream1 = Stream.of("hello", "world", "hello world"); List<String> collect = stream1.collect(Collectors.toList());// 自己是一個終止操做 collect.forEach(System.out::println); System.out.println("- - - - - - "); //使用原生的 collect 來將流轉成List Stream<String> stream2 = Stream.of("hello", "world", "hello world"); // List<String> lis = stream2.collect(() -> new ArrayList(), (theList, item) -> theList.add(item), // (theList1, theList2) -> theList1.addAll(theList2)); // 將上面的轉換成方法引用的方式 -- 這種方法很差理解. List<String> list = stream2.collect(LinkedList::new, LinkedList::add, LinkedList::addAll); //這種方法,若是想要返回ArrayList也能夠實現. // List<String> list1 = stream2.collect(ArrayList::new, ArrayList::add, ArrayList::addAll); list.forEach(System.out::println); }
public static void main(String[] args) { Stream<String> stream = Stream.of("hello", "world", "hello world"); //將流轉換成List 另外一種方法 // List<String> list= stream.collect(Collectors.toCollection(ArrayList::new)); // list.forEach(System.out::println); //將流轉成set // Set<String> set = stream.collect(Collectors.toSet()); //轉成TreeSet // TreeSet<String> set = stream.collect(Collectors.toCollection(TreeSet::new)); // set.forEach(System.out::println); //轉成字符串 String string = stream.collect(Collectors.joining()); System.out.println(string); //Collectors 類中有多重輔助的方法. }
遇到問題的時候,先思考一下可否用方法引用的方式,使用流的方式來操做。由於用起來比較簡單。
public static void main(String[] args) { //將集合中的每個元素 轉換成大寫的字母, 給輸出來 List<String> list = Arrays.asList("hello","world","hello world"); //轉成字符串,而後轉成大寫. // System.out.println(list.stream().collect(Collectors.joining()).toUpperCase()); //上面的代碼 能夠轉換成下邊的代碼. // System.out.println(String.join("", list).toUpperCase()); //視頻上給出的 仍是List的大寫 list.stream().map(String::toUpperCase).collect(Collectors.toList()).forEach(System.out::println); //將集合 的數據給平方一下輸出. List<Integer> list1 = Arrays.asList(1, 2, 3, 4, 5); list1.stream().map(item -> item * item).collect(Collectors.toList()).forEach(System.out::println); }
流中的 .map () 方法,是對集合中的每個數據進行一下操做。
public static void main(String[] args) { // 舉例: flag 的操做, 打平. 一個集合中有三個數組, 打平以後,三個數組的元素依次排列. Stream<List<Integer>> stream = Stream.of(Arrays.asList(1), Arrays.asList(2, 3), Arrays.asList(4, 5)); //將裏邊每個ArrayList的數據 作一個平方. 而後打平. 輸出一個list stream.flatMap(theList -> theList.stream()).map(item -> item * item).forEach(System.out::println); }
public static void main(String[] args) { // stream 其餘方法介紹. // generate(). 生成stream對象 Stream<String> stream = Stream.generate(UUID.randomUUID()::toString); // System.out.println(stream.findFirst().get()); // findFirst,找到第一個對象.而後就短路了,會返回一個Optional對象(爲了不NPE),不符合函數式編程 // stream.findFirst().isPresent(System.out::print); // iterate() 會生成 一個 無限的串行流. // 通常不會單獨使用. 會使用limit 來限制一下總長度. Stream.iterate(1, item -> item + 2).limit(6).forEach(System.out::println); }
Stream<Integer> stream = Stream.iterate(1, item -> item + 2).limit(6); //找出該流中大於2的元素,先使用filter()過濾. //每一個元素*2 使用mapToInt 避免重複拆箱. //忽略掉流中的前兩個元素; 使用 skip(2) //再取流中的前兩個元素; 使用limit(2) //求出流元素中的總和. 使用sum() System.out.println(stream.filter(item -> item>2).mapToInt(item -> item * 2).skip(2).limit(2).sum());
// .min() 返回的是IntOptional. // System.out.println(stream.filter(item -> item>2).mapToInt(item -> item * 2).skip(2).limit(2).min()); //應該這樣調用. 上邊的可能會出NPE異常 stream.filter(item -> item>2).mapToInt(item -> item * 2).skip(2).limit(2).min().ifPresent(System.out::println);
在練習的過程當中發現了一個問題。若是是這樣連續打印兩條對流操做以後的結果。會報流未關閉的異常。
如何避免:使用方法鏈的方式來處理流。 具體出現的緣由,後續進行詳細的源碼講解。
public static void main(String[] args) { List<String> list = Arrays.asList("hello", "world", "hello world"); //首字母轉大寫 list.stream().map(item ->{ String s = item.substring(0, 1).toUpperCase() + item.substring(1); System.out.println("test"); return s; }).forEach(System.out::println); //沒有遇到停止操做時,是不會執行中間操做的.是延遲的 // 遇到.forEach() 停止操做時,纔會執行中間操做的代碼 }
//程序不會中止 IntStream.iterate(0,i->(i+1)%2).distinct().limit(6).forEach(System.out::println); //程序會中止 IntStream.iterate(0,i->(i+1)%2).limit(6).distinct().forEach(System.out::println);
和迭代器不一樣的是,Stream能夠並行化操做,迭代器只能命令式地、串行化操做
- 當使用穿行方式去遍歷時,每一個item讀完後再讀下一個item
- 使用並行去遍歷時,數據會被分紅多個段,其中每個都在不一樣的線程中處理,而後將結果一塊兒輸出。
Stream的並行操做依賴於Java7中引入的Fork/Join框架。
描述性的語言:sql和Stream的對比
select name from student where age > 20 and address = 'beijing' order by desc;
===================================================================================
Student.stream().filter(student -> student.getAge >20 ).filter(student -> student.getAddress().equals("beijing")).sorted(..).forEach(student -> System.out.println(student.getName));
上述的描述,並無明確的告訴底層具體要怎麼作,只是發出了描述性的信息。這種流的方式就叫作內部迭代。針對於性能來講,流的操做確定不會下降性能。
外邊迭代舉例: jdk8之前的用的方式。
List
list = new ArrayList<>(); for(int i = 0 ;i <= students.size();i++){
Student student = students.get(i);
If(student.getAge() > 20 )
list.add(student);
}
Collections.sort(list.....)
list.forEach().....
Stream的出現和集合是密不可分的。
集合關注的是數據與數據存儲自己,流關注的則是對數據的計算。
流與迭代器相似的一點是:流是沒法重複使用或消費的。
中間操做都會返回一個Stream對象,好比說返回Stream
停止操做則不會返回Stream類型,可能不返回值,也可能返回其餘類型的單個值。
public static void main(String[] args) { // 串行流和並行流的比較 List<String> list = new ArrayList<>(5000000); for (int i = 0; i < 5000000; i++) { list.add(UUID.randomUUID().toString()); } System.out.println("開始排序"); long startTime = System.nanoTime(); // list.parallelStream().sorted().count(); //串行流 list.parallelStream().sorted().count(); //並行流 long endTime = System.nanoTime(); long millis = TimeUnit.NANOSECONDS.toMillis(endTime - startTime); System.out.println("排序時間爲: "+ millis); }
結果如圖,並行流和串行流時間上錯了4倍。
public static void main(String[] args) { List<String> list = Arrays.asList("hello", "world", "hello world"); // list.stream().mapToInt(item -> item.length()).filter(length -> length ==5) // .findFirst().ifPresent(System.out::println); list.stream().mapToInt(item -> { int length = item.length(); System.out.println(item); return length; }).filter(length -> length == 5).findFirst().ifPresent(System.out::println); //返回的是hello , 不包含 world. }
返回的是hello , 不包含 world.
流的操做原理: 把流想成一個容器,裏邊存儲的是對每個元素的操做。操做時,把操做串行化。對同一個元素進行串行的操做。操做中還包含着短路操做。
public static void main(String[] args) { //舉例; 找出 這個集合中全部的單詞,並且要去重. List<String> list = Arrays.asList("hello welcome", "world hello", "hello world", "hello hello world"); // list.stream().map(item -> item.split(" ")).distinct() // .collect(Collectors.toList()).forEach(System.out::println); //使用map不能知足需求, 使用flatMap list.stream().map(item -> item.split(" ")).flatMap(Arrays::stream) .distinct().collect(Collectors.toList()).forEach(System.out::println); //結果爲 hello welcome world }
public static void main(String[] args) { //組合起來. 打印出 hi zhangsan , hi lisi , hi wangwu , hello zhangsan , hello lisi .... List<String> list = Arrays.asList("Hi", "Hello", "你好"); List<String> list1 = Arrays.asList("zhangsan", "lisi", "wangwu"); List<String> collect = list.stream().flatMap(item -> list1.stream().map(item2 -> item + " " + item2)).collect(Collectors.toList()); collect.forEach(System.out::println); }
public static void main(String[] args) { //數據準備. Student student1 = new Student("zhangsan", 100, 20); Student student2 = new Student("lisi", 90, 20); Student student3 = new Student("wangwu", 90, 30); Student student4 = new Student("zhangsan", 80, 40); List<Student> students = Arrays.asList(student1, student2, student3, student4); //對學生按照姓名分組. Map<String, List<Student>> listMap = students.stream().collect(Collectors.groupingBy(Student::getName)); System.out.println(listMap); //對學生按照分數分組. Map<Integer, List<Student>> collect = students.stream().collect(Collectors.groupingBy(Student::getScore)); System.out.println(collect); //按照年齡分組. Map<Integer, List<Student>> ageMap = students.stream().collect(Collectors.groupingBy(Student::getAge)); System.out.println(ageMap); //按照名字分組後,獲取到每一個分組的元素的個數. Map<String, Long> nameCount = students.stream().collect(Collectors.groupingBy(Student::getName, Collectors.counting())); System.out.println(nameCount); //按照名字分組,求得每一個組的平均值. Map<String, Double> doubleMap = students.stream().collect(Collectors.groupingBy(Student::getName, Collectors.averagingDouble(Student::getScore))); System.out.println(doubleMap); //分區, 分組的一種特例. 只能分兩個組 true or flase . partitioning By Map<Boolean, List<Student>> collect1 = students.stream().collect(Collectors.partitioningBy(student -> student.getScore() >= 90)); System.out.println(collect1); }