Java8(1)新特性介紹及Lambda表達式
這,僅是我學習過程中記錄的筆記。確定了一個待研究的主題,對這個主題進行全方面的剖析。筆記是用來方便我回顧與學習的,歡迎大家與我進行交流溝通,共同成長。不止是技術。
前言:
跟大娃一塊看,把原來的電腦拿出來放中間看視頻用
--- 以后會有的課程 難度
- 深入Java 8 難度1
- 並發與netty 難度3
- JVM 難度4
- node 難度2
- spring精髓 難度1
課程中提到的知識:
前后端分離的開發,是靠node當做中間的
netty,已經成為國內外互聯網公司的標配。會涉及底層的源代碼的理解。
JVM 涉及的東西比較多。雖然天天用,但是沒有深入理解過。各種鎖,可見性等。與計算機原理息息相關的。
聖思園主要面對與已經工作的。大部分為一線的開發人員。
課程一定是完整的。由淺入深的。一定要有一種耐心。
對於基礎不好的,可以看看以前面授的時候錄制的視頻。不懂的一定要多查資料。
在講課過程中的設計思路:4000塊錢的收費標准。
jdk8
介紹:Java 8可謂Java語言歷史上變化最大的一個版本,其承諾要調整Java編程向着函數式風格邁進,這有助於編寫出更為簡潔、表達力更強,並且在很多情況下能夠利用並行硬件的代碼。本門課程將會深入介紹Java 8新特性,學員將會通過本門課程的學習深入掌握Java 8新增特性並能靈活運用在項目中。學習者將學習到如何通過Lambda表達式使用一行代碼編寫Java函數,如何通過這種功能使用新的Stream API進行編程,如何將冗長的集合處理代碼壓縮為簡單且可讀性更好的流程序。學習創建和消費流的機制,分析其性能,能夠判斷何時應該調用API的並行執行特性。
課程的介紹:
- Java 8新特性介紹
- Lambda表達式介紹
- 使用Lambda表達式代替匿名內部類
- Lambda表達式的作用
- 外部迭代與內部迭代
- Java Lambda表達式語法詳解
- 函數式接口詳解
- 傳遞值與傳遞行為
- Stream深度解析
- Stream API詳解
- 串行流與並行流
- Stream構成
- Stream源生成方式
- Stream操作類型
- Stream轉換
- Optional詳解
- 默認方法詳解
- 方法與構造方法引用
- Predicate接口詳解
- Function接口詳解
- Consumer接口剖析
- Filter介紹
- Map-Reduce講解、中間操作與終止操作
- 新的Date API分析
拉姆達表達式: 函數式編程。以前的叫做命令式的編程。
使用面向對象語言就是來操作數據,封裝繼承多態。
函數式編程面向的是行為。好處:代碼可讀性提高。
開發安卓的時候大量的匿名內部類。
提到的關鍵字:
kotlin ,JetBrains 。construction 構造
他以前在學習的時候,翻代碼。
將要講解的各個技術的簡介、
課程講解的時候遇到的工具:
Mac , jdk8 ,idea(很多功能是通過插件的形式來實現的)
Java8課程開始
lambda表達式
為什么要使用lambda表示式
- 在Java中無法將函數座位參數傳遞給一個方法,也無法返回一個函數的方法。
- 在js中,函數的參數是一個函數。返回值是另一個函數的情況是非常常見的。是一門經典的函數式語言。
Java匿名內部類。
匿名內部類的介紹
Gradle的使用。可以完全使用maven的中央倉庫。
進行安卓的開發時,gradle已經成為標配了。
lambda:
匿名內部類
my_jButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Button Pressed! ");
}
});
改造后
my_jButton.addActionListener(e -> System.out.println("Button Pressed!"));
lambda表達式的基本結構:
(param1,param2,param3) ->{
}
函數式編程: 一個接口里邊只有一個抽象方法。
可以通過lambda表達式來實例。
關於函數式接口:
- 如果一個借口只有一個抽象方法,那么該接口就是一個函數式接口。
- 如果我們在某一個接口上聲明了functionalInterface注解,那么編譯器就會按照函數是借口的定義來要求改接口。
- 如果某個接口只有一個抽象方法,但是我們並沒有給接口聲明functionnaleInterface注解,編譯器依舊會給改接口看作是函數式接口。
通過實例對函數式接口的理解:
package com.erwa.jdk8;
@FunctionalInterface
interface MyInterface {
void test();
// Multiple non-overriding abstract methods found in interface com.erwa.jdk8.MyInterface
// void te();
//如果一個接口聲明一個抽象方法,但是這個方法重寫了 object類中的一個方法.
//接口的抽象方法不會加一.所以依然是函數方法.
// Object 類是所有類的父類.
@Override
String toString();
}
public class Test2 {
public void myTest(MyInterface myInterface) {
System.out.println(1);
myInterface.test();
System.out.println(2);
}
public static void main(String[] args) {
Test2 test2 = new Test2();
test2.myTest(() -> {
System.out.println(3);
});
}
}
接口里邊從1.8開始也可以有方法實現了。default
默認方法。
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
* <p>Note that instances of functional interfaces can be created with
* lambda expressions, method references, or constructor reference
lambda表達式的作用:
- lambda表達式為Java添加了確實的函數式編程特性,使我們能將函數當做一等公民看待。
- 在將函數座位一等公民的語言中,lambda表達式的類型是函數。但是在Java中,lambda表達式是對象,他們必須依附於一類特別的對象類型-函數式接口(function interface)
迭代的方式:
- 外部迭代:
- 內部迭代:
- 方法引用:
list.forEach(System.out::println);
接口中可以有默認方法和靜態方法。
流: stream
/**
* Returns a sequential {@code Stream} with this collection as its source.
*
* <p>This method should be overridden when the {@link #spliterator()}
* method cannot return a spliterator that is {@code IMMUTABLE},
* {@code CONCURRENT}, or <em>late-binding</em>. (See {@link #spliterator()}
* for details.)
*
* @implSpec
* The default implementation creates a sequential {@code Stream} from the
* collection's {@code Spliterator}.
*
* @return a sequential {@code Stream} over the elements in this collection
* @since 1.8
*/
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
關於流方式實現的舉例:
public static void main(String[] args) {
//函數式接口的實現方式
MyInterface1 i1 = () -> {};
System.out.println(i1.getClass().getInterfaces()[0]);
MyInterface2 i2 = () -> {};
System.out.println(i2.getClass().getInterfaces()[0]);
// 沒有上下文對象,一定會報錯的.
// () -> {};
//通過lambda來實現一個線程.
new Thread(() -> System.out.println("hello world")).start();
//有一個list ,將內容中的首字母變大寫輸出.
List<String> list = Arrays.asList("hello","world","hello world");
//通過lambda來實現所有字母編程大寫輸出.
// list.forEach(item -> System.out.println(item.toUpperCase()));
//把三個單詞放入到新的集合里邊.
List<String> list1 = new ArrayList<>(); //diamond語法. 后邊的<>不用再放類型
// list.forEach(item -> list1.add(item.toUpperCase()));
// list1.forEach(System.out::println);
//進一步的改進. 流的方式
// list.stream();//單線程
// list.parallelStream(); //多線程
list.stream().map(item -> item.toUpperCase()).forEach(System.out::println);//單線程
list.stream().map(String::toUpperCase).forEach(System.out::println);
//上邊的兩種方法,都滿足函數式接口的方式.
}
lambda表達式的作
- 傳遞行為,而不僅僅是值
- 提升抽象層次
- API重用性更好
- 更加靈活
lambda基本語法
- (argument) -> (body)
- 如: (arg1,arg2...) -> (body)
Java lambda結構
- 一個Lambda表達式可以有0個或者多個參數
- 參數的類型既可以明確聲明,也可以根據上下文來推斷。例如:(int a) 與 (a) 效果相同
- 所有參數包含在圓括號內,參數之間用逗號相隔。
- 空圓括號代表參數集為空。
- 當只有一個參數,且類型可推倒時。圓括號()可省略。
- lambda表達式的主體可以包含0條或多條語句。
- 如果lambda表達式的主體只有一條語句,花括號{}可以省略,匿名函數的返回類型與該主體表達式一致。
- 如果lambda表達式的主體包含一條以上語句,則表達式必須包含在花括號中。匿名函數的韓繪制類型與代碼塊的返回類型一致,諾沒有反回則為空。
高階函數:
如果一個函數接收一個函數作為參數,或者返回一個函數作為返回值,那么該函數就叫做高階函數.
傳遞行為的舉例:
public static void main(String[] args) {
// 函數的測試
// 傳遞行為的一種方式.
FunctionTest functionTest = new FunctionTest();
int compute = functionTest.compute(1, value -> 2 * value);
System.out.println(compute);
System.out.println(functionTest.compute(2,value -> 5+ value));
System.out.println(functionTest.compute(3,a -> a * a));
System.out.println(functionTest.convert(5, a -> a + "hello "));
/**
* 高階函數:
* 如果一個函數接收一個函數作為參數,或者返回一個函數作為返回值,那么該函數就叫做高階函數.
*/
}
//使用lambda表達式的話,可以直覺預定義行為.用的時候傳遞.
// 即 函數式編程.
public int compute(int a, Function<Integer, Integer> function) {
return function.apply(a);
}
public String convert(int a, Function<Integer, String> function) {
return function.apply(a);
}
// 之前完成行為的做法. 提前把行為定義好,用的時候調用方法. 如:
public int method1(int a ){
return a * 2 ;
}
Function類中提供的默認方法的講解:
/**
* Returns a composed function that first applies the {@code before}
* function to its input, and then applies this function to the result.
* If evaluation of either function throws an exception, it is relayed to
* the caller of the composed function.
返回一個組合的函數。對應用完參數后的結果,再次運行apply
*
* @param <V> the type of input to the {@code before} function, and to the
* composed function
* @param before the function to apply before this function is applied
* @return a composed function that first applies the {@code before}
* function and then applies this function
* @throws NullPointerException if before is null
*
* @see #andThen(Function)
*/
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
/**
* Returns a composed function that first applies this function to
* its input, and then applies the {@code after} function to the result.
* If evaluation of either function throws an exception, it is relayed to
* the caller of the composed function.
*
* @param <V> the type of output of the {@code after} function, and of the
* composed function
* @param after the function to apply after this function is applied
* @return a composed function that first applies this function and then
* applies the {@code after} function
* @throws NullPointerException if after is null
*
* @see #compose(Function)
*/
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
compose : 組合function, 形成兩個function的串聯。 先執行參數
andThen :先應用當前的函數apply,然后再當做參數再次執行apply。 后執行參數。
identity:輸入什么返回什么。
BiFunction: 整合兩個函數的方法。
為什么BiFunction不提供 compose ,只提供andThen呢?
因為如果提供compose方法的話,只能獲取一個參數的返回值。不合理。
public static void main(String[] args) {
FunctionTest2 functionTest2 = new FunctionTest2();
// compose
// System.out.println(functionTest2.compute(2,a -> a * 3,b -> b * b));
// andThen
// System.out.println(functionTest2.compute2(2,a -> a * 3,b -> b * b));
//BiFunction
// System.out.println(functionTest2.compute3(1,2, (a,b) -> a - b));
// System.out.println(functionTest2.compute3(1,2, (a,b) -> a * b));
// System.out.println(functionTest2.compute3(1,2, (a,b) -> a + b));
// System.out.println(functionTest2.compute3(1,2, (a,b) -> a / b));
//BiFunction andThen
System.out.println(functionTest2.compute4(2,3,(a,b) ->a + b , a -> a * a ));
}
//compose : 組合function, 形成兩個function的串聯。 先執行參數
//andThen :先應用當前的函數apply,然后再當做參數再次執行apply。 后執行參數
public int compute(int a, Function<Integer, Integer> function1, Function<Integer, Integer> function2) {
return function1.compose(function2).apply(a);
}
public int compute2(int a, Function<Integer, Integer> function1, Function<Integer, Integer> function2) {
return function1.andThen(function2).apply(a);
}
//BiFunction
//求兩個參數的和
//先定義一個抽象的行為.
public int compute3(int a, int b, BiFunction<Integer, Integer, Integer> biFunction) {
return biFunction.apply(a, b);
}
//BiFunction andThen
public int compute4(int a, int b, BiFunction<Integer, Integer, Integer> biFunction, Function<Integer, Integer> function) {
return biFunction.andThen(function).apply(a, b);
}
測試 函數式接口的實例:
public class PersonTest {
public static void main(String[] args) {
List<Person> personList = new ArrayList<>();
personList.add(new Person("zhangsan", 20));
personList.add(new Person("zhangsan", 28));
personList.add(new Person("lisi", 30));
personList.add(new Person("wangwu", 40));
PersonTest test = new PersonTest();
//測試 getPersonUsername
// List<Person> personList1 = test.getPersonUsername("zhangsan", personList);
// personList1.forEach(person -> System.out.println(person.getUsername()));
//測試 getPersonByAge
List<Person> personByAge = test.getPersonByAge(25, personList);
personByAge.forEach(person -> System.out.println(person.getAge()));
//測試第三種: 自定義輸入行為
List<Person> list = test.getPersonByAge2(20,personList,(age,persons) ->{
return persons.stream().filter(person -> person.getAge() > age).collect(Collectors.toList());
});
list.forEach(person -> System.out.println(person.getAge()));
}
public List<Person> getPersonUsername(String username, List<Person> personList) {
return personList.stream().filter(person -> person.getUsername().equals(username)).collect(Collectors.toList());
}
public List<Person> getPersonByAge(int age, List<Person> personList) {
//使用BiFunction的方式
// BiFunction<Integer, List<Person>, List<Person>> biFunction = (ageOfPerson, list) -> {
// return list.stream().filter(person -> person.getAge() > ageOfPerson ).collect(Collectors.toList());
// };
//變換之后:
BiFunction<Integer, List<Person>, List<Person>> biFunction = (ageOfPerson, list) ->
list.stream().filter(person -> person.getAge() > ageOfPerson ).collect(Collectors.toList());
return biFunction.apply(age, personList);
}
//第三種方式, 動作也讓用戶自己定義傳進來
public List<Person> getPersonByAge2(int age ,List<Person> list,BiFunction<Integer,List<Person>,List<Person>> biFunction){
return biFunction.apply(age, list);
}
}
函數式接口的真諦: 傳遞的是行為,而不是數據
。
public static void main(String[] args) {
//給定一個輸入參數,判斷是否滿足條件,滿足的話返回true
Predicate<String> predicate = p -> p.length() > 5;
System.out.println(predicate.test("nnihaoda"));
}
到現在為止,只是講解了Java.lang.function包下的幾個最重要的,經常使用的方法。
2020年01月01日19:03:33 新的一年開始,記錄一下每次學習的時間。
Predicate 謂語。 類中包含的方法:
-
boolean test(T t);
-
default Predicate<T> or(Predicate<? super T> other)
-
default Predicate<T> negate()
-
default Predicate<T> and(Predicate<? super T> other)
-
static <T> Predicate<T> isEqual(Object targetRef)
函數式編程,注重傳遞行為,而不是傳遞值。
public class PredicateTest2 {
/**
* 測試Predicate中的test方法
*/
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
PredicateTest2 predicateTest2 = new PredicateTest2();
//獲取大於5的數字
predicateTest2.getAllFunction(list,item -> item > 5);
System.out.println("--------");
//獲取所有的偶數
predicateTest2.getAllFunction(list,item -> item % 2 ==0);
System.out.println("--------");
//獲取所有的數字
predicateTest2.getAllFunction(list,item -> true);
//獲取大於5並且是偶數的
System.out.println("--------");
predicateTest2.testAnd(list,item -> item > 5,item -> item % 2 == 0);
}
public void getAllFunction(List<Integer> list, Predicate<Integer> predicate){
for (Integer integer : list) {
if (predicate.test(integer)) {
System.out.println(integer);
}
}
}
// test or and
public void testAnd(List<Integer> list,Predicate<Integer> integerPredicate,Predicate<Integer> integerPredicate1){
for (Integer integer : list) {
if (integerPredicate.and(integerPredicate1).test(integer)) {
System.out.println(integer);
}
}
}
}
lambda表達式到底給我們帶來了什么?原來通過面向對象的時候一個方法只能執行一種功能。現在傳遞的是行為,一個方法可以多次調用。
邏輯與或非三種的理解.
Supplier類 供應廠商;供應者 (不接收參數,返回結果)
用於什么場合? 工廠
2020年1月3日08:06:28
BinaryOperator 接口
public class SinaryOpertorTest {
public static void main(String[] args) {
SinaryOpertorTest sinaryOpertorTest = new SinaryOpertorTest();
System.out.println(sinaryOpertorTest.compute(1,2,(a,b) -> a+b));
System.out.println("-- -- - - - -- -");
System.out.println(sinaryOpertorTest.getMax("hello123","world",(a,b) -> a.length() - b.length()));
}
private int compute(int a, int b, BinaryOperator<Integer> binaryOperator) {
return binaryOperator.apply(a, b);
}
private String getMax(String a, String b, Comparator<String> comparator) {
return BinaryOperator.maxBy(comparator).apply(a, b);
}
}
Optional final :Optional 不要試圖用來當做參數, 一般只用來接收返回值,來規避值的空指針異常的問題。
- empty()
- of()
- ofNullable()
- isPresent()
- get()
- ...
public class OptionalTest {
public static void main(String[] args) {
Optional<String> optional = Optional.of("hello");
//不確定是否為 空是 調用和這個方法
// Optional<String> optional2 = Optional.ofNullable("hello");
// Optional<String> optional1 = Optional.empty();
//過時
// if (optional.isPresent()) {
// System.out.println(optional.get());
// }
optional.ifPresent(item -> System.out.println(item));
System.out.println(optional.orElse("nihao"));
System.out.println(optional.orElseGet(() -> "nihao"));
}
public class OptionalTest2 {
public static void main(String[] args) {
Employee employee = new Employee();
employee.setName("dawa");
Employee employee1 = new Employee();
employee1.setName("erwa");
List<Employee> list = Arrays.asList(employee, employee1);
Company company = new Company("gongsi", list);
Optional<Company> optionalCompany = Optional.ofNullable(company);
System.out.println(optionalCompany.map(company1 -> company1.getList()).orElse(Collections.emptyList()));
}
}
Java8(2)方法引用詳解及Stream流介紹
2020你還不會Java8新特性?方法引用詳解及Stream 流介紹和操作方式詳解(三)
方法引用詳解
方法引用: method reference
方法引用實際上是Lambda表達式的一種語法糖
我們可以將方法引用看作是一個「函數指針」,function pointer
方法引用共分為4類:
- 類名::靜態方法名
- 引用名(對象名)::實例方法名
- 類名::實例方法名 (比較不好理解,個地方調用的方法只有一個參數,為什么還能正常調用呢? 因為調用比較時,第一個對象來調用getStudentByScore1. 第二個對象來當做參數)
- 構造方法引用: 類名::new
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 method
默認方法是指實現此接口時,默認方法已經被默認實現。
引入默認方法最重要的作用就是Java要保證向后兼容。
情景一: 一個類,實現了兩個接口。兩個接口中有一個相同名字的默認方法。此時會報錯,需要從寫這個重名的方法
情景二: 約定:實現類的優先級比接口的優先級要高。 一個類,實現一個接口,繼承一個實現類。接口和實現類中有一個同名的方法,此時,此類會使用實現類中的方法。
Stream 流介紹和操作方式詳解
Collection提供了新的stream()方法。
流不存儲值,通過管道的方式獲取值。
本質是函數式的,對流的操作會生成一個結果,不過並不會修改底層的數據源,集合可以作為流的底層數據源。
延遲查找,很多流操作(過濾、映射、排序等)等可以延遲實現。
通過流的方式可以更好的操作集合。使用函數式編程更為流程。與lambda表達式搭配使用。
流由3部分構成:
- 源
- 零個或多個中間操作(操作的是誰?操作的是源)
- 終止操作(得到一個結果)
流操作的分類:
- 惰性求值(中間操作)
- 及早求值(種植操作)
使用鏈式的調用方式sunc as : stream.xxx().yyy().zzz().count(); 沒有count的時候前邊的三個方法不會被調用。后續會進行舉例。
掌握流常用的api,了解底層。
流支持並行化,可以多線程操作。迭代器不支持並行化。
流怎么用?
流的創建方式
- 通過靜態方法 : Stream stream = Stream.of();
- 通過數組:Arrays.stream();
- 通過集合創建對象:Stream stream = list.stream;
流的簡單應用
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));
}
函數式編程和傳統面向對象編程根本上有什么不同?
傳統面向對象編程傳遞的是數據。函數式編程通過方法傳遞的是一種行為,行為指導了函數的處理,根據行為對數據進行加工。
舉例:流轉換成list的練習
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);
}
Collectors類中包含了流轉換的多個輔助類
舉例: 將流 轉成各種類型的數據。
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 () 方法,是對集合中的每一個數據進行一下操作。
stream 的 flat操作。 打平操作。
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);
}
Stream 其他方法介紹:
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 運算練習:(Stream提供了各種操作符)
舉例:找出該流中大於2的元素,然后每個元素*2 ,然后忽略掉流中的前兩個元素,然后再取流中的前兩個元素,最后求出流元素中的總和.
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());
舉例:找出該流中大於2的元素,然后每個元素*2 ,然后忽略掉流中的前兩個元素,然后再取流中的前兩個元素,最后找到最小的元素.
// .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);
舉例:獲取最大值,最小值,求和等各種操作。 .summaryStatistics();
在練習的過程中發現了一個問題。如果是這樣連續打印兩條對流操作之后的結果。會報流未關閉的異常。
注意事項:流被重復使用了,或者流被關閉了,就會出異常。
如何避免:使用方法鏈的方式來處理流。 具體出現的原因,后續進行詳細的源碼講解。
舉例 :中間操作(惰性求值) 和中止操作(及早求值)本質的區別
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底層深入
和迭代器不同的是,Stream可以並行化操作,迭代器只能命令式地、串行化操作
當使用穿行方式去遍歷時,每個item讀完后再讀下一個item
使用並行去遍歷時,數據會被分成多個段,其中每一個都在不同的線程中處理,然后將結果一起輸出。
Stream的並行操作依賴於Java7中引入的Fork/Join框架。
流(Stream)由3部分構成:
- 源(Source)
- 零個或多個中間操作(Transforming values)(操作的是誰?操作的是源)
- 終止操作(Operations)(得到一個結果)
內部迭代和外部迭代
描述性的語言: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倍。
舉例: 打印出列表中出來第一個長度為5的單詞.. 同時將長度5打印出來.
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.
流的操作原理: 把流想成一個容器,里邊存儲的是對每一個元素的操作。操作時,把操作串行化。對同一個元素進行串行的操作。操作中還包含着短路操作。
舉例: 找出 這個集合中所有的單詞,而且要去重. flatMap()的使用。
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
}
舉例:組合起來. 打印出 hi zhangsan , hi lisi , hi wangwu , hello zhangsan , hello lisi .... flatMap()的使用。
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);
}
舉例: 流對分組/分區操作的支持. group by / protition by
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);
}
Java8(3)Collector類源碼分析
繼續學習Java8 新特性。
Collector類源碼分析2020了你還不會Java8新特性?
jdk8是怎么對底層完成支持的。不了解底層,平時用還可以,但是遇到問題的時候就會卡在那里。遲遲滅有解決方案。在學習一門新技術時,先學習怎么去用,不要執着於源碼。但是隨着用的越來越多,你去了解底層是比較好的一種學習方法。
有多種方法可以實現同一個功能.什么方式更好呢? 越具體的方法越好. 減少自動裝箱拆箱操作
- collect : 收集器
- Collector作為collect方法的參數。
- Collector作為一個接口。它是一個可變的匯聚操作,將輸入元素累計到一個可變的結果容器中;它會在所有元素都處理完畢后將累計的結果作為一個最終的表示(這是一個可選操作);它支持串行與並行兩種方式執行。(並不是說並行一定比串行快。)
- Collects本身提供了關於Collectoe的常見匯聚實現,Collectors本身實際上是一個工廠。
- 為了確保串行和並行的結果一致,需要進行額外的處理。必須要滿足兩個約束。
identity 同一性
associativity 結合性 - 同一性:對於任何一條並行線路來說 ,需要滿足a == combiner.apply(a, supplier.get())。舉例來說:
(Listlist1,List list2 -> {list1.addAll(list2);return list1})
結合性: 下方有舉例。
Collector收集器的實現源碼詳解
/**
* A <a href="package-summary.html#Reduction">mutable reduction operation</a> that
* accumulates input elements into a mutable result container, optionally transforming
* the accumulated result into a final representation after all input elements
* have been processed. Reduction operations can be performed either sequentially
* or in parallel.
Collector作為一個接口。它是一個可變的匯聚操作,將輸入元素累計到一個可變的結果容器中;它會在所有元素都處理 完畢后將累計的結果作為一個最終的表示(這是一個可選操作);它支持串行與並行兩種方式執行。(並不是說並行一定比串行快。)
* <p>Examples of mutable reduction operations include:
* accumulating elements into a {@code Collection}; concatenating
* strings using a {@code StringBuilder}; computing summary information about
* elements such as sum, min, max, or average; computing "pivot table" summaries
* such as "maximum valued transaction by seller", etc. The class {@link Collectors}
* provides implementations of many common mutable reductions.
Collects本身提供了關於Collectoe的常見匯聚實現,Collectors本身實際上是一個工廠。
* <p>A {@code Collector} is specified by four functions that work together to
* accumulate entries into a mutable result container, and optionally perform
* a final transform on the result. They are: <ul>
* <li>creation of a new result container ({@link #supplier()})</li>
* <li>incorporating a new data element into a result container ({@link #accumulator()})</li>
* <li>combining two result containers into one ({@link #combiner()})</li>
* <li>performing an optional final transform on the container ({@link #finisher()})</li>
* </ul>
Collector 包含了4個參數
* <p>Collectors also have a set of characteristics, such as
* {@link Characteristics#CONCURRENT}, that provide hints that can be used by a
* reduction implementation to provide better performance.
*
* <p>A sequential implementation of a reduction using a collector would
* create a single result container using the supplier function, and invoke the
* accumulator function once for each input element. A parallel implementation
* would partition the input, create a result container for each partition,
* accumulate the contents of each partition into a subresult for that partition,
* and then use the combiner function to merge the subresults into a combined
* result.
舉例說明:
1,2, 3, 4 四個部分結果。
1,2 -》 5
5,3 -》 6
6,4 -》 6
### 同一性和結合性的解析:
* <p>To ensure that sequential and parallel executions produce equivalent
* results, the collector functions must satisfy an <em>identity</em> and an
* <a href="package-summary.html#Associativity">associativity</a> constraints.
為了確保串行和並行的結果一致,需要進行額外的處理。必須要滿足兩個約束。
identity 同一性
associativity 結合性
* <p>The identity constraint says that for any partially accumulated result,
* combining it with an empty result container must produce an equivalent
* result. That is, for a partially accumulated result {@code a} that is the
* result of any series of accumulator and combiner invocations, {@code a} must
* be equivalent to {@code combiner.apply(a, supplier.get())}.
同一性: 對於任何一條並行線路來說,需要滿足a == combiner.apply(a, supplier.get())
* <p>The associativity constraint says that splitting the computation must
* produce an equivalent result. That is, for any input elements {@code t1}
* and {@code t2}, the results {@code r1} and {@code r2} in the computation
* below must be equivalent:
* <pre>{@code
* A a1 = supplier.get(); 串行:
* accumulator.accept(a1, t1); 第一個參數,每次累加的中間結果。 第二個參數,下一個要處理的參數
* accumulator.accept(a1, t2);
* R r1 = finisher.apply(a1); // result without splitting
*
* A a2 = supplier.get(); 並行:
* accumulator.accept(a2, t1); 第一個參數,每次累加的中間結果。 第二個參數,下一個要處理的參數
* A a3 = supplier.get();
* accumulator.accept(a3, t2);
* R r2 = finisher.apply(combiner.apply(a2, a3)); // result with splitting
* } </pre>
結合性: 如上例。 最終要求 r1 == r2
* <p>For collectors that do not have the {@code UNORDERED} characteristic,
* two accumulated results {@code a1} and {@code a2} are equivalent if
* {@code finisher.apply(a1).equals(finisher.apply(a2))}. For unordered
* collectors, equivalence is relaxed to allow for non-equality related to
* differences in order. (For example, an unordered collector that accumulated
* elements to a {@code List} would consider two lists equivalent if they
* contained the same elements, ignoring order.)
對於無序的收集器來說,等價性就被放松了,會考慮到順序上的區別對應的不相等性。
兩個集合中包含了相同的元素,但是忽略了順序。這種情況下兩個的集合也是等價的。
### collector復合與注意事項:
* <p>Libraries that implement reduction (匯聚) based on {@code Collector}, such as
* {@link Stream#collect(Collector)}, must adhere to the following constraints:
* <ul>
* <li>The first argument passed to the accumulator function, both
* arguments passed to the combiner function, and the argument passed to the
* finisher function must be the result of a previous invocation of the
* result supplier, accumulator, or combiner functions.</li>
* <li>The implementation should not do anything with the result of any of
* the result supplier, accumulator, or combiner functions other than to
* pass them again to the accumulator, combiner, or finisher functions,
* or return them to the caller of the reduction operation.</li>
具體的實現來說,不應該對中間返回的結果進行額外的操作。除了最終的返回的結果。
* <li>If a result is passed to the combiner or finisher
* function, and the same object is not returned from that function, it is
* never used again.</li>
如果一個結果被傳遞給combiner or finisher,但是並沒有返回一個你傳遞的對象,說明你生成了一個新的結果或者創建了新的對象。這個結果就不會再被使用了。
* <li>Once a result is passed to the combiner or finisher function, it
* is never passed to the accumulator function again.</li>
一旦一個結果被傳遞給了 combiner or finisher 函數,他就不會再被傳遞給了accumulator函數了。
* <li>For non-concurrent collectors, any result returned from the result
* supplier, accumulator, or combiner functions must be serially
* thread-confined. This enables collection to occur in parallel without
* the {@code Collector} needing to implement any additional synchronization.
* The reduction implementation must manage that the input is properly
* partitioned, that partitions are processed in isolation, and combining
* happens only after accumulation is complete.</li>
線程和線程之間的處理都是獨立的,最終結束時再進行合並。
* <li>For concurrent collectors, an implementation is free to (but not
* required to) implement reduction concurrently. A concurrent reduction
* is one where the accumulator function is called concurrently from
* multiple threads, using the same concurrently-modifiable result container,
* rather than keeping the result isolated during accumulation.
* A concurrent reduction should only be applied if the collector has the
* {@link Characteristics#UNORDERED} characteristics or if the
* originating data is unordered.</li>
如果不是並發收集器,4個線程會生成4個中間結果。
是並發收集器的話,4個線程會同時調用一個結果容器。
* </ul>
*
* <p>In addition to the predefined implementations in {@link Collectors}, the
* static factory methods {@link #of(Supplier, BiConsumer, BinaryOperator, Characteristics...)}
* can be used to construct collectors. For example, you could create a collector
* that accumulates widgets into a {@code TreeSet} with:
*
* <pre>{@code
* Collector<Widget, ?, TreeSet<Widget>> intoSet =
* Collector.of(TreeSet::new, TreeSet::add,
* (left, right) -> { left.addAll(right); return left; });
* }</pre>
通過Collector.of(傳進一個新的要操作的元素,結果容器處理的步驟,多線程處理的操作)
將流中的每個Widget 添加到TreeSet中
* (This behavior is also implemented by the predefined collector
* {@link Collectors#toCollection(Supplier)}).
*
* @apiNote
* Performing a reduction operation with a {@code Collector} should produce a
* result equivalent to:
* <pre>{@code
* R container = collector.supplier().get();
* for (T t : data)
* collector.accumulator().accept(container, t);
* return collector.finisher().apply(container);
* }</pre>
api的說明: collector的finisher匯聚的實現過程。
* <p>However, the library is free to partition the input, perform the reduction
* on the partitions, and then use the combiner function to combine the partial
* results to achieve a parallel reduction. (Depending on the specific reduction
* operation, this may perform better or worse, depending on the relative cost
* of the accumulator and combiner functions.)
性能取決於accumulator and combiner的代價。 也就是說 並行流 並不一定比串行流效率高。
* <p>Collectors are designed to be <em>composed</em>; many of the methods
* in {@link Collectors} are functions that take a collector and produce
* a new collector. For example, given the following collector that computes
* the sum of the salaries of a stream of employees:
* <pre>{@code
* Collector<Employee, ?, Integer> summingSalaries
* = Collectors.summingInt(Employee::getSalary))
* }</pre>
搜集器是可以組合的: take a collector and produce a new collector.
搜集器的實現過程。 如 員工的工資的求和。
* If we wanted to create a collector to tabulate the sum of salaries by
* department, we could reuse the "sum of salaries" logic using
* {@link Collectors#groupingBy(Function, Collector)}:
* <pre>{@code
* Collector<Employee, ?, Map<Department, Integer>> summingSalariesByDept
* = Collectors.groupingBy(Employee::getDepartment, summingSalaries);
* }</pre>
如果我們想要新建一個搜集器,我們可以復用之前的搜集器。
實現過程。
* @see Stream#collect(Collector)
* @see Collectors
*
* @param <T> the type of input elements to the reduction operation
<T> 代表 流中的每一個元素的類型。
* @param <A> the mutable accumulation type of the reduction operation (often
* hidden as an implementation detail)
<A> 代表 reduction操作的可變容器的類型。表示中間操作生成的結果的類型(如ArrayList)。
* @param <R> the result type of the reduction operation
<R> 代表 結果類型
* @since 1.8
*/
public interface Collector<T, A, R>{
/**
* A function that creates and returns a new mutable result container.
* A就代表每一次返回結果的類型
* @return a function which returns a new, mutable result container
*/
Supplier<A> supplier(); // 提供一個結果容器
/**
* A function that folds a value into a mutable result container.
* A代表中間操作返回結果的類型。 T是下一個代操作的元素的類型。
* @return a function which folds a value into a mutable result container
*/
BiConsumer<A, T> accumulator(); //不斷的向結果容器中添加元素。
/**
* A function that accepts two partial results and merges them. The
* combiner function may fold state from one argument into the other and
* return that, or may return a new result container.
* A 中間操作返回結果的類型。
* @return a function which combines two partial results into a combined
* result
*/
BinaryOperator<A> combiner(); //在多線程中 合並 部分結果。
/**
和並行流緊密相關的
接收兩個結果,將兩個部分結果合並到一起。
combiner函數,有4個線程同時去執行,那么就會有生成4個部分結果。
舉例說明:
1,2, 3, 4 四個部分結果。
1,2 -》 5
5,3 -》 6
6,4 -》 6
1,2合並返回5 屬於return a new result container.
6,4合並返回6,屬於The combiner function may fold state from one argument into the other and return that。
*/
/**
* Perform the final transformation from the intermediate accumulation type
* {@code A} to the final result type {@code R}.
*R 是最終返回結果的類型。
* <p>If the characteristic {@code IDENTITY_TRANSFORM} is
* set, this function may be presumed to be an identity transform with an
* unchecked cast from {@code A} to {@code R}.
*
* @return a function which transforms the intermediate result to the final
* result
*/
Function<A, R> finisher(); // 合並中間的值,給出返回值。
/**
* Returns a {@code Set} of {@code Collector.Characteristics} indicating
* the characteristics of this Collector. This set should be immutable.
*
* @return an immutable set of collector characteristics
*/
Set<Characteristics> characteristics(); //特征的集合
/**
* Returns a new {@code Collector} described by the given {@code supplier},
* {@code accumulator}, and {@code combiner} functions. The resulting
* {@code Collector} has the {@code Collector.Characteristics.IDENTITY_FINISH}
* characteristic.
*
* @param supplier The supplier function for the new collector
* @param accumulator The accumulator function for the new collector
* @param combiner The combiner function for the new collector
* @param characteristics The collector characteristics for the new
* collector
* @param <T> The type of input elements for the new collector
* @param <R> The type of intermediate accumulation result, and final result,
* for the new collector
* @throws NullPointerException if any argument is null
* @return the new {@code Collector}
*/
public static<T, R> Collector<T, R, R> of(Supplier<R> supplier,
BiConsumer<R, T> accumulator,
BinaryOperator<R> combiner,
Characteristics... characteristics) {
Objects.requireNonNull(supplier);
Objects.requireNonNull(accumulator);
Objects.requireNonNull(combiner);
Objects.requireNonNull(characteristics);
Set<Characteristics> cs = (characteristics.length == 0)
? Collectors.CH_ID
: Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH,
characteristics));
return new Collectors.CollectorImpl<>(supplier, accumulator, combiner, cs);
}
/**
* Returns a new {@code Collector} described by the given {@code supplier},
* {@code accumulator}, {@code combiner}, and {@code finisher} functions.
*
* @param supplier The supplier function for the new collector
* @param accumulator The accumulator function for the new collector
* @param combiner The combiner function for the new collector
* @param finisher The finisher function for the new collector
* @param characteristics The collector characteristics for the new
* collector
* @param <T> The type of input elements for the new collector
* @param <A> The intermediate accumulation type of the new collector
* @param <R> The final result type of the new collector
* @throws NullPointerException if any argument is null
* @return the new {@code Collector}
*/
public static<T, A, R> Collector<T, A, R> of(Supplier<A> supplier,
BiConsumer<A, T> accumulator,
BinaryOperator<A> combiner,
Function<A, R> finisher,
Characteristics... characteristics) {
Objects.requireNonNull(supplier);
Objects.requireNonNull(accumulator);
Objects.requireNonNull(combiner);
Objects.requireNonNull(finisher);
Objects.requireNonNull(characteristics);
Set<Characteristics> cs = Collectors.CH_NOID;
if (characteristics.length > 0) {
cs = EnumSet.noneOf(Characteristics.class);
Collections.addAll(cs, characteristics);
cs = Collections.unmodifiableSet(cs);
}
return new Collectors.CollectorImpl<>(supplier, accumulator, combiner, finisher, cs);
}
/**
* Characteristics indicating properties of a {@code Collector}, which can
* be used to optimize reduction implementations.
*/
enum Characteristics { // 特征
/**
* Indicates that this collector is <em>concurrent</em>, meaning that
* the result container can support the accumulator function being
* called concurrently with the same result container from multiple
* threads.
* 並發的,同一個結果容器可以由多個線程同時調用。
* <p>If a {@code CONCURRENT} collector is not also {@code UNORDERED},
* then it should only be evaluated concurrently if applied to an
* unordered data source.
如果不是UNORDERED。只能用於無序的數據源。
如果不加CONCURRENT,還是可以操作並行流。但是操作的不是一個結果容器,而是多個結果容器。則需要調用finisher.
如果加了CONCURRENT,則是多個線程操作同一結果容器。 則無需調用finisher.
*/
CONCURRENT,
/**
* Indicates that the collection operation does not commit to preserving
* the encounter order of input elements. (This might be true if the
* result container has no intrinsic order, such as a {@link Set}.)
收集操作並不保留順序。
*/
UNORDERED,
/**
* Indicates that the finisher function is the identity function and
* can be elided. If set, it must be the case that an unchecked cast
* from A to R will succeed.
如果用和這個參數,表示 Finish函數就是 identity函數。 並且轉換一定要是成功的。
*/
IDENTITY_FINISH
}
}
Java8(4)(五)收集器比較器用法詳解及源碼剖析
收集器用法詳解與多級分組和分區
為什么在collectors類中定義一個靜態內部類?
static class CollectorImpl<T, A, R> implements Collector<T, A, R>
設計上,本身就是一個輔助類,是一個工廠。作用是給開發者提供常見的收集器實現。提供的方法都是靜態方法,可以直接調用。
函數式編程最大的特點:表示做什么,而不是如何做。開發者更注重如做什么,底層實現如何做。
/**
* Implementations of {@link Collector} that implement various useful reduction
* operations, such as accumulating elements into collections, summarizing
* elements according to various criteria, etc.
沒有實現的方法,可以自己去編寫收集器。
* <p>The following are examples of using the predefined collectors to perform
* common mutable reduction tasks:
* 舉例:
* <pre>{@code
* // Accumulate names into a List 名字加入到一個集合。
* List<String> list = people.stream().map(Person::getName).collect(Collectors.toList());
*
* // Accumulate names into a TreeSet 名字加入到一個Set。 待排序的集合。
* Set<String> set = people.stream().map(Person::getName).collect(Collectors.toCollection(TreeSet::new));
*
* // Convert elements to strings and concatenate them, separated by commas
* String joined = things.stream()
* .map(Object::toString)
* .collect(Collectors.joining(", "));
*
* // Compute sum of salaries of employee 計算員工工資的總數。
* int total = employees.stream()
* .collect(Collectors.summingInt(Employee::getSalary)));
*
* // Group employees by department 對員工進行分組。
* Map<Department, List<Employee>> byDept
* = employees.stream()
* .collect(Collectors.groupingBy(Employee::getDepartment));
*
* // Compute sum of salaries by department 根據部門計算工資的總數。
* Map<Department, Integer> totalByDept
* = employees.stream()
* .collect(Collectors.groupingBy(Employee::getDepartment,
* Collectors.summingInt(Employee::getSalary)));
*
* // Partition students into passing and failing 將學生進行分區。
* Map<Boolean, List<Student>> passingFailing =
* students.stream()
* .collect(Collectors.partitioningBy(s -> s.getGrade() >= PASS_THRESHOLD));
*
* }</pre>
*
* @since 1.8 提供了常見的方法。沒有的話可以去自定義。
*/
public final class Collectors {
舉例。collector中的方法應用:
public static void main(String[] args) {
Student student1 = new Student("zhangsan", 80);
Student student2 = new Student("lisi", 90);
Student student3 = new Student("wangwu", 100);
Student student4 = new Student("zhaoliu", 90);
Student student5 = new Student("zhaoliu", 90);
List<Student> students = Arrays.asList(student1, student2, student3, student4, student5);
//list 轉換成一個流,再轉換成一個集合.
List<Student> students1 = students.stream().collect(Collectors.toList());
students1.forEach(System.out::println);
System.out.println("- - - - - - -");
// collect 方法底層原理介紹.
//有多種方法可以實現同一個功能.什么方式更好呢? 越具體的方法越好. 減少自動裝箱拆箱操作.
System.out.println("count:" + students.stream().collect(Collectors.counting()));
System.out.println("count:" + (Long) students.stream().count());
System.out.println("- - - - - - - -");
//舉例練習
// 找出集合中分數最低的學生,打印出來.
students.stream().collect(minBy(Comparator.comparingInt(Student::getScore))).ifPresent(System.out::println);
// 找出集合中分數最大成績
students.stream().collect(maxBy(Comparator.comparingInt(Student::getScore))).ifPresent(System.out::println);
// 求平均值
System.out.println(students.stream().collect(averagingInt(Student::getScore)));
// 求分數的綜合
System.out.println(students.stream().collect(summingInt(Student::getScore)));
// 求各種匯總信息 結果為IntSummaryStatistics{count=5, sum=450, min=80, average=90.000000, max=100}
System.out.println(students.stream().collect(summarizingInt(Student::getScore)));
System.out.println(" - - - - - ");
// 字符串的拼接 結果為:zhangsanlisiwangwuzhaoliuzhaoliu
System.out.println(students.stream().map(Student::getName).collect(joining()));
//拼接加分隔符 結果為:zhangsan,lisi,wangwu,zhaoliu,zhaoliu
System.out.println(students.stream().map(Student::getName).collect(joining(",")));
// 拼接加前后綴 結果為:hello zhangsan,lisi,wangwu,zhaoliu,zhaoliu world
System.out.println(students.stream().map(Student::getName).collect(joining(",", "hello ", " world")));
System.out.println("- - - - - - ");
// group by 多層分組
// 根據分數和名字進行分組 輸出結果為:
// {80={zhangsan=[Student{name='zhangsan', score=80}]},
// 100={wangwu=[Student{name='wangwu', score=100}]},
// 90={lisi=[Student{name='lisi', score=90}], zhaoliu=[Student{name='zhaoliu', score=90}, Student{name='zhaoliu', score=90}]}}
Map<Integer, Map<String, List<Student>>> collect = students.stream().collect(groupingBy(Student::getScore, groupingBy(Student::getName)));
System.out.println(collect);
System.out.println("- - - - - - - ");
// partitioningBy 多級分區 輸出結果為:{false=[Student{name='zhangsan', score=80}], true=[Student{name='lisi', score=90}, Student{name='wangwu', score=100}, Student{name='zhaoliu', score=90}, Student{name='zhaoliu', score=90}]}
Map<Boolean, List<Student>> collect1 = students.stream().collect(partitioningBy(student -> student.getScore() > 80));
System.out.println(collect1);
// 按照大於80分區,再按照90分區
//輸出結果為:{false={false=[Student{name='zhangsan', score=80}], true=[]}, true={false=[Student{name='lisi', score=90}, Student{name='zhaoliu', score=90}, Student{name='zhaoliu', score=90}], true=[Student{name='wangwu', score=100}]}}
Map<Boolean, Map<Boolean, List<Student>>> collect2 = students.stream().collect(partitioningBy(student -> student.getScore() > 80, partitioningBy(student -> student.getScore() > 90)));
System.out.println(collect2);
//分區, 然后求出每個分組中的個數. 結果為:{false=1, true=4}
Map<Boolean, Long> collect3 = students.stream().collect(partitioningBy(student -> student.getScore() > 80, counting()));
System.out.println(collect3);
System.out.println("- - - - - - - ");
//根據名字分組,得到學生的分數 --, 使用collectingAndThen 求最小值,然后整合起來. 最后Optional.get()一定有值.
students.stream().collect(groupingBy(Student::getName,collectingAndThen(minBy(Comparator.comparingInt(Student::getScore)), Optional::get)));
}
Comparator比較器詳解與類型推斷特例
Comparator 比較器。引用了多個default方法。
完成一個功能時有多個方法,使用特化的方法。因為效率會更高。減少了裝箱拆箱的操作。減少性能損耗。
舉例: 簡單功能實現
public static void main(String[] args) {
List<String> list = Arrays.asList("nihao", "hello", "world", "welcome");
//對list按照字母的升序排序
// list.stream().sorted().forEach(System.out::println);
//按照字符串的長度排序
// Collections.sort(list, (item1, item2) -> item1.length() - item2.length());
// Collections.sort(list, Comparator.comparingInt(String::length));
//字符串的降序排序
// list.sort(Comparator.comparingInt(String::length).reversed());
// 下邊的形式會報錯 item識別成了(Obejct).
//lambda表達式的類型推斷. 如果無法推斷類型,需要自己制定類型
// list.sort(Comparator.comparingInt(item-> item.length()).reversed());
//這樣寫就成功了.
list.sort(Comparator.comparingInt((String item )-> item.length()).reversed());
//為什么這個地方無法推斷類型?
// 能推斷出的 : list.stream().... Strean<T> 傳遞的有參數. 精確的類型可以進行類型推斷.
//這個地方沒有明確具體是什么類型.ToIntFunction<? super T> .可以是String 或者在往上的父類 這個地方看成了Object類了.
// list.sort(Comparator.comparingInt((Boolean item)-> 1).reversed());
//這種Boolean 就會報錯.編譯不通過.
System.out.println(list);
}
比較器深入舉例練習
舉例:兩層的比較.先按照字符串的長度升序排序. 長度相同,根據每一個ASCII碼的順序排序、
thenComparing()多級排序的練習。;
List<String> list = Arrays.asList("nihao", "hello", "world", "welcome");
//兩層的比較.先按照字符串的長度升序排序. 長度相同,根據每一個ASCII碼的升序排序. (不區分大小寫的 ,按照字母排序的規則) 幾種實現的方法。
list.sort(Comparator.comparingInt(String::length).thenComparing(String.CASE_INSENSITIVE_ORDER));
list.sort(Comparator.comparingInt(String::length).thenComparing((item1,item2) -> item1.toUpperCase().compareTo(item2.toUpperCase())));
list.sort(Comparator.comparingInt(String::length).thenComparing(Comparator.comparing(String::toUpperCase)));
//排序后將順序翻轉過來. reverseOrder();
list.sort(Comparator.comparingInt(String::length).thenComparing(String::toLowerCase,Comparator.reverseOrder()));
// 按照字符串的長度降序排序, 再根據ASCII的降序排序
list.sort(Comparator.comparingInt(String::length).reversed()
.thenComparing(String::toLowerCase,Comparator.reverseOrder()));
//多級排序
list.sort(Comparator.comparingInt(String::length).reversed()
.thenComparing(String::toLowerCase, Comparator.reverseOrder())
.thenComparing(Comparator.reverseOrder()));
// 最后一個thenComparing()沒有發生作用。
自定義一個簡單的收集器
jdk提供了Collector接口。
public class MySetCollector<T> implements Collector<T,Set<T>,Set<T>> {
@Override
public Supplier<Set<T>> supplier() {
//用於提供一個空的容器
System.out.println("supplier invoked! ");
return HashSet::new; // 不接受對象,返回一個Set對象
}
@Override
public BiConsumer<Set<T>, T> accumulator() {
// 累加器類型. 接收兩個參數不返回值.
//完成的功能: 不斷的往set中添加元素
System.out.println("accumulator invoked! ");
return Set<T>::add ;
// return HashSet<T>::add ; 返回HashSet報錯. 原因: 返回的是中間類型的返回類型. 不論返回什么類型的Set ,Set都符合要求.
}
@Override
public BinaryOperator<Set<T>> combiner() {
//將並行流的多個結果給合並起來.
System.out.println("combiner invoked! ");
return (set1,set2)->{
set1.addAll(set2);
return set1;
};
}
@Override
public Function<Set<T>, Set<T>> finisher() {
//完成器,把所有的結果都合並在一起. 返回一個最終的結果類型
//如果中間類型 和最終結果類型一致, 不執行此方法;
System.out.println("finisher invoked! ");
// return t -> t ;
return Function.identity(); // 總是返回參數.
}
@Override
public Set<Characteristics> characteristics() {
System.out.println("characterstics invoked! ");
return Collections.unmodifiableSet(EnumSet.of(Characteristics.IDENTITY_FINISH,Characteristics.UNORDERED)); // 這個地方 不給參數,IDENTITY_FINISH . 則會調用finisher()
}
public static void main(String[] args) {
List<String> list = Arrays.asList("hello", "world");
Set<String> collect = list.stream().collect(new MySetCollector<>());
System.out.println(collect);
}
輸出結果為:
supplier invoked!
accumulator invoked!
combiner invoked!
characterstics invoked!
characterstics invoked!
[world, hello]
}
接下來跟源碼,看一下程序的調用過程。
@Override
@SuppressWarnings("unchecked")
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);
}
自定義收集器的深度剖析與並行缺陷
// 舉例: 需求:將一個Set,進行一個收集.對結果進行增強,封裝在一個map當中. // 輸入:Set<String> // 輸出:Map<String,String> // 示例輸入: [hello,world,hello world] // 示例輸出: {[hello,hello],[world,world],[hello world,hello world]}
public class MySetCollector2<T> implements Collector<T, Set<T>, Map<T, T>> {
@Override
public Supplier<Set<T>> supplier() {
System.out.println("supplier invoked!");
return HashSet::new;
}
@Override
public BiConsumer<Set<T>, T> accumulator() {
System.out.println("accumlator invoked!");
return (set, item) -> {
set.add(item);
//每次調用 打印出線程 這里會打印6次,
System.out.println("accunlator : " +set+ ", "+ Thread.currentThread().getName());
//出現異常的原因在這里:
// 一個線程去修改一個集合,同時另外一個線程去迭代它(遍歷它)。程序就會拋出並發修改異常。如果是並行操作的話,就不要在操作中額外的添加操作。添加就添加,別再去打印他。
};
}
@Override
public BinaryOperator<Set<T>> combiner() {
System.out.println("combiner invoked!");
//並行流的時候才會被調用. 將並行流的多個結果給合並起來
return (set1, set2) -> {
set1.addAll(set2);
return set2;
};
}
@Override
public Function<Set<T>, Map<T, T>> finisher() {
System.out.println("finisher invoked!");
// 中間類型和最終類型 一樣,這個是不會被調用的.
//這里不一樣 . 會進行調用
return set -> {
Map<T, T> map = new HashMap<>();
// Map<T, T> map = new TreeMap<>(); 直接返回一個排序的Map
set.forEach(item -> map.put(item,item));
return map;
};
}
@Override
public Set<Characteristics> characteristics() {
System.out.println(" characteristics invoked");
return Collections.unmodifiableSet(EnumSet.of(Characteristics.UNORDERED));// 這個參數不能亂寫. 要理解每個枚舉的具體意思.
// return Collections.unmodifiableSet(EnumSet.of(Characteristics.UNORDERED,Characteristics.CONCURRENT));// 這個參數不能亂寫. 要理解每個枚舉的具體意思.
//加了這個參數 Characteristics.CONCURRENT
// 會出異常, 會正常運行. Caused by: java.util.ConcurrentModificationException
// return Collections.unmodifiableSet(EnumSet.of(Characteristics.UNORDERED,Characteristics.IDENTITY_FINISH));
// 加了參數Characteristics.IDENTITY_FINISH . 會報錯
// Process 'command '/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/bin/java'' finished with non-zero exit value 1
// IDENTITY_FINISH 實際的含義: 如果用和這個參數,表示 Finish函數就是 identity函數。 並且轉換一定要是成功的。失敗的話會拋異常.
// 這個收集器具有什么特性 ,由Characteristics 來定義. 就算你賦值的不實際,他也照樣執行.
}
public static void main(String[] args) {
List<String> list = Arrays.asList("hello","hello", "world", "helloworld","1","4","j");
Set<String> set = new HashSet<>(list);
System.out.println("set"+set);
// Map<String, String> collect = set.stream().collect(new MySetCollector2<>());
Map<String, String> collect = set.parallelStream().collect(new MySetCollector2<>()); //並行流
System.out.println(collect);
}
}
並行流缺陷詳解
並行:
accumlator invoked!
accunlator : [j], main
accunlator : [j, hello], main
accunlator : [helloworld, 4, j, hello], ForkJoinPool.commonPool-worker-2
accunlator : [helloworld, 1, 4, j, hello], ForkJoinPool.commonPool-worker-2
accunlator : [helloworld, 1, world, 4, j, hello], ForkJoinPool.commonPool-worker-2
串行。
accunlator : [j], main
accunlator : [helloworld], ForkJoinPool.commonPool-worker-11
accunlator : [helloworld, 1], ForkJoinPool.commonPool-worker-11
accunlator : [helloworld, 1, world], ForkJoinPool.commonPool-worker-11
accunlator : [4], ForkJoinPool.commonPool-worker-9
accunlator : [j, hello], main
/**
* Characteristics indicating properties of a {@code Collector}, which can
* be used to optimize reduction implementations.
*/
enum Characteristics { // 特征
/**
* Indicates that this collector is <em>concurrent</em>, meaning that
* the result container can support the accumulator function being
* called concurrently with the same result container from multiple
* threads.
* 並發的,同一個結果容器可以由多個線程同時調用。
* <p>If a {@code CONCURRENT} collector is not also {@code UNORDERED},
* then it should only be evaluated concurrently if applied to an
* unordered data source.
如果不是UNORDERED。只能用於無序的數據源。
如果不加CONCURRENT,還是可以操作並行流。但是操作的不是一個結果容器,而是多個結果容器。則需要調用finisher.
如果加了CONCURRENT,則是多個線程操作同一結果容器。 則無需調用finisher.
*/
CONCURRENT,
/**
* Indicates that the collection operation does not commit to preserving
* the encounter order of input elements. (This might be true if the
* result container has no intrinsic order, such as a {@link Set}.)
收集操作並不保留順序。無序的。
*/
UNORDERED,
/**
* Indicates that the finisher function is the identity function and
* can be elided. If set, it must be the case that an unchecked cast
* from A to R will succeed.
如果用和這個參數,表示 Finish函數就是 identity函數。 並且轉換一定要是成功的。不會調用Finish方法
*/
IDENTITY_FINISH
}
出異常的根本原因:
一個線程去修改一個集合,同時另外一個線程去迭代它(遍歷它)。程序就會拋出並發修改異常。
如果是並行操作的話,就不要在操作中額外的添加操作。添加就添加,別再去打印他。
如果不加CONCURRENT,還是可以操作並行流。但是操作的不是一個結果容器,而是多個結果容器。則需要調用finisher.
如果加了CONCURRENT,則是多個線程操作同一結果容器。 則無需調用finisher.
超線程介紹:
超線程(HT, Hyper-Threading)是英特爾研發的一種技術,於2002年發布。超線程技術原先只應用於Xeon 處理器中,當時稱為“Super-Threading”。之后陸續應用在Pentium 4 HT中。早期代號為Jackson。 [1]
通過此技術,英特爾實現在一個實體CPU中,提供兩個邏輯線程。之后的[Pentium D](https://baike.baidu.com/item/Pentium D)縱使不支持超線程技術,但就集成了兩個實體核心,所以仍會見到兩個線程。超線程的未來發展,是提升處理器的邏輯線程。英特爾於2016年發布的Core i7-6950X便是將10核心的處理器,加上超線程技術,使之成為20個邏輯線程的產品
收集器總結:
Collectors類中方法的實現練習。收集器總是有中間的容器。有必要的總結一下收集器中的方法。
當你具備一些前提的東西之后,你再去看難的東西就會覺得理所當然的。
對於Collectors靜態工廠類來說,實現一共分為兩種情況:
-
通過CollectorImpl來實現。
-
通過reducing方法來實現;reducing方法本身又是通過CollectorImpl實現的。
總的來說,都是通過CollectorImpl來實現的。
1. toCollection(collectionFactory) 。 將集合轉成指定的集合。
public static <T, C extends Collection<T>>
Collector<T, ?, C> toCollection(Supplier<C> collectionFactory) {
return new CollectorImpl<>(collectionFactory, Collection<T>::add,
(r1, r2) -> { r1.addAll(r2); return r1; },
CH_ID);
}
2. toList()是 toCollection()方法的一種具體實現。
public static <T>
Collector<T, ?, List<T>> toList() {
return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add,
(left, right) -> { left.addAll(right); return left; },
CH_ID);
}
3. toSet() 是toCollection()方法的一種具體實現。
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);
}
4. joining(); 融合成一個字符串。還有兩個重載的,單參數的和多參數的
public static Collector<CharSequence, ?, String> joining() {
return new CollectorImpl<CharSequence, StringBuilder, String>(
StringBuilder::new, StringBuilder::append,
(r1, r2) -> { r1.append(r2); return r1; },
StringBuilder::toString, CH_NOID);
}
public static Collector<CharSequence, ?, String> joining(CharSequence delimiter) {
return joining(delimiter, "", "");
}
public static Collector<CharSequence, ?, String> joining(CharSequence delimiter,
CharSequence prefix,
CharSequence suffix) {
return new CollectorImpl<>(
() -> new StringJoiner(delimiter, prefix, suffix),
StringJoiner::add, StringJoiner::merge,
StringJoiner::toString, CH_NOID);
}
5.mapping(); 將收集器的A 映射成B
public static <T, U, A, R>
Collector<T, ?, R> mapping(Function<? super T, ? extends U> mapper,
Collector<? super U, A, R> downstream) {
BiConsumer<A, ? super U> downstreamAccumulator = downstream.accumulator();
return new CollectorImpl<>(downstream.supplier(),
(r, t) -> downstreamAccumulator.accept(r, mapper.apply(t)),
downstream.combiner(), downstream.finisher(),
downstream.characteristics());
}
such as :
Map<City, Set<String>> lastNamesByCity
= people.stream().collect(groupingBy(Person::getCity, mapping(Person::getLastName, toSet())));
6.collectingAndThen(); 收集處理轉換完后, 再去進行一個轉換。
public static<T,A,R,RR> Collector<T,A,RR> collectingAndThen(Collector<T,A,R> downstream,
Function<R,RR> finisher) {
Set<Collector.Characteristics> characteristics = downstream.characteristics();
if (characteristics.contains(Collector.Characteristics.IDENTITY_FINISH)) {
if (characteristics.size() == 1)
characteristics = Collectors.CH_NOID;
else {
characteristics = EnumSet.copyOf(characteristics);
characteristics.remove(Collector.Characteristics.IDENTITY_FINISH);
// 這個地方為什么要把IDENTITY_FINISH 去掉。
// 如果不去掉的話, 最終結果直接返回中間結果的類型
characteristics = Collections.unmodifiableSet(characteristics);
}
}
return new CollectorImpl<>(downstream.supplier(),
downstream.accumulator(),
downstream.combiner(),
downstream.finisher().andThen(finisher),
characteristics);
}
such as :
List<String> people
= people.stream().collect(collectingAndThen(toList(),Collections::unmodifiableList));
7. counting(); 計數。
public static <T> Collector<T, ?, Long>
counting() {
return reducing(0L, e -> 1L, Long::sum);
}
8. 最大值最小值
public static <T> Collector<T, ?, Optional<T>>
minBy(Comparator<? super T> comparator) {
return reducing(BinaryOperator.minBy(comparator));
}
public static <T> Collector<T, ?, Optional<T>>
maxBy(Comparator<? super T> comparator) {
return reducing(BinaryOperator.maxBy(comparator));
}
9. summingInt();求和。
public static <T> Collector<T, ?, Integer>
summingInt(ToIntFunction<? super T> mapper) {
return new CollectorImpl<>(
() -> new int[1], // 這個地方為什么不可以用一個0,來當做中間類型呢?數字本身是一個值類型的,不可變的,沒法引用。數組本身是一個引用類型,可以進行傳遞。數組本身是一個容器。
(a, t) -> { a[0] += mapper.applyAsInt(t); },
(a, b) -> { a[0] += b[0]; return a; },
a -> a[0], CH_NOID);
}
public static <T> Collector<T, ?, Long>
summingLong(ToLongFunction<? super T> mapper) {
return new CollectorImpl<>(
() -> new long[1],
(a, t) -> { a[0] += mapper.applyAsLong(t); },
(a, b) -> { a[0] += b[0]; return a; },
a -> a[0], CH_NOID);
}
public static <T> Collector<T, ?, Double>
summingDouble(ToDoubleFunction<? super T> mapper) {
/*
* In the arrays allocated for the collect operation, index 0
* holds the high-order bits of the running sum, index 1 holds
* the low-order bits of the sum computed via compensated
* summation, and index 2 holds the simple sum used to compute
* the proper result if the stream contains infinite values of
* the same sign.
*/
return new CollectorImpl<>(
() -> new double[3],
(a, t) -> { sumWithCompensation(a, mapper.applyAsDouble(t));
a[2] += mapper.applyAsDouble(t);},
(a, b) -> { sumWithCompensation(a, b[0]);
a[2] += b[2];
return sumWithCompensation(a, b[1]); },
a -> computeFinalSum(a),
CH_NOID);
}
10. averagingInt(); 求平均值。
public static <T> Collector<T, ?, Double>
averagingInt(ToIntFunction<? super T> mapper) {
return new CollectorImpl<>(
() -> new long[2],
(a, t) -> { a[0] += mapper.applyAsInt(t); a[1]++; },
(a, b) -> { a[0] += b[0]; a[1] += b[1]; return a; },
a -> (a[1] == 0) ? 0.0d : (double) a[0] / a[1], CH_NOID);
}
public static <T> Collector<T, ?, Double>
averagingLong(ToLongFunction<? super T> mapper) {
return new CollectorImpl<>(
() -> new long[2],
(a, t) -> { a[0] += mapper.applyAsLong(t); a[1]++; },
(a, b) -> { a[0] += b[0]; a[1] += b[1]; return a; },
a -> (a[1] == 0) ? 0.0d : (double) a[0] / a[1], CH_NOID);
}
public static <T> Collector<T, ?, Double>
averagingDouble(ToDoubleFunction<? super T> mapper) {
/*
* In the arrays allocated for the collect operation, index 0
* holds the high-order bits of the running sum, index 1 holds
* the low-order bits of the sum computed via compensated
* summation, and index 2 holds the number of values seen.
*/
return new CollectorImpl<>(
() -> new double[4],
(a, t) -> { sumWithCompensation(a, mapper.applyAsDouble(t)); a[2]++; a[3]+= mapper.applyAsDouble(t);},
(a, b) -> { sumWithCompensation(a, b[0]); sumWithCompensation(a, b[1]); a[2] += b[2]; a[3] += b[3]; return a; },
a -> (a[2] == 0) ? 0.0d : (computeFinalSum(a) / a[2]),
CH_NOID);
}
11. reducing() ; 詳解。
public static <T> Collector<T, ?, T>
reducing(T identity, BinaryOperator<T> op) {
return new CollectorImpl<>(
boxSupplier(identity),
(a, t) -> { a[0] = op.apply(a[0], t); },
(a, b) -> { a[0] = op.apply(a[0], b[0]); return a; },
a -> a[0],
CH_NOID);
}
12. groupingBy(); 分組方法詳解。
public static <T, K> Collector<T, ?, Map<K, List<T>>> //使用者本身不注重中間類型怎么操作。
groupingBy(Function<? super T, ? extends K> classifier) {
return groupingBy(classifier, toList()); //調用兩個參數的 groupingBy();
}
* @param <T> the type of the input elements //T; 接收的類型。
* @param <K> the type of the keys // K,分類器函數中間返回結果的類型。
* @param <A> the intermediate accumulation type of the downstream collector
* @param <D> the result type of the downstream reduction
*
public static <T, K, A, D>
Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier,
Collector<? super T, A, D> downstream) {
return groupingBy(classifier, HashMap::new, downstream); // 調用三參數的 groupingBy()
}
//功能最完全的groupingBy();
/**
* Returns a {@code Collector} implementing a cascaded "group by" operation
* on input elements of type {@code T}, grouping elements according to a
* classification function, and then performing a reduction operation on
* the values associated with a given key using the specified downstream
* {@code Collector}. The {@code Map} produced by the Collector is created
* with the supplied factory function.
*
* <p>The classification function maps elements to some key type {@code K}.
* The downstream collector operates on elements of type {@code T} and
* produces a result of type {@code D}. The resulting collector produces a
* {@code Map<K, D>}.
*
* <p>For example, to compute the set of last names of people in each city,
* where the city names are sorted:
* <pre>{@code
* Map<City, Set<String>> namesByCity
* = people.stream().collect(groupingBy(Person::getCity, TreeMap::new,
* mapping(Person::getLastName, toSet())));
* }</pre>
*
* @implNote
* The returned {@code Collector} is not concurrent. For parallel stream
* pipelines, the {@code combiner} function operates by merging the keys
* from one map into another, which can be an expensive operation. If
* preservation of the order in which elements are presented to the downstream
* collector is not required, using {@link #groupingByConcurrent(Function, Supplier, Collector)}
* may offer better parallel performance.
* 返回的 並不是並發的。如果順序並不是很重要的話, 推薦使用groupingByConcurrent(); 並發的分組函數。
* @param <T> the type of the input elements
* @param <K> the type of the keys
* @param <A> the intermediate accumulation type of the downstream collector
* @param <D> the result type of the downstream reduction
* @param <M> the type of the resulting {@code Map}
* @param classifier a classifier function mapping input elements to keys
* @param downstream a {@code Collector} implementing the downstream reduction
* @param mapFactory a function which, when called, produces a new empty
* {@code Map} of the desired type
* @return a {@code Collector} implementing the cascaded group-by operation
*
* @see #groupingBy(Function, Collector)
* @see #groupingBy(Function)
* @see #groupingByConcurrent(Function, Supplier, Collector)
*/
public static <T, K, D, A, M extends Map<K, D>>
Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> classifier,
Supplier<M> mapFactory,
Collector<? super T, A, D> downstream) {
Supplier<A> downstreamSupplier = downstream.supplier();
BiConsumer<A, ? super T> downstreamAccumulator = downstream.accumulator();
BiConsumer<Map<K, A>, T> accumulator = (m, t) -> {
K key = Objects.requireNonNull(classifier.apply(t), "element cannot be mapped to a null key");
A container = m.computeIfAbsent(key, k -> downstreamSupplier.get());
downstreamAccumulator.accept(container, t);
};
BinaryOperator<Map<K, A>> merger = Collectors.<K, A, Map<K, A>>mapMerger(downstream.combiner()); //接收兩個參數,參會一個結果。
@SuppressWarnings("unchecked")
Supplier<Map<K, A>> mangledFactory = (Supplier<Map<K, A>>) mapFactory; // 進行一個強制的類型轉換。
if (downstream.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)) {
//如果 IDENTITY_FINISH , 則不用調用finisher方法。
return new CollectorImpl<>(mangledFactory, accumulator, merger, CH_ID);
}
else {
@SuppressWarnings("unchecked")
Function<A, A> downstreamFinisher = (Function<A, A>) downstream.finisher();
Function<Map<K, A>, M> finisher = intermediate -> {
intermediate.replaceAll((k, v) -> downstreamFinisher.apply(v));
@SuppressWarnings("unchecked")
M castResult = (M) intermediate;
return castResult;
};
return new CollectorImpl<>(mangledFactory, accumulator, merger, finisher, CH_NOID);
}
}
13. groupingByConcurrent(); 並發的分組方法。 使用前提是對數據里邊的順序沒有要求。
/**
* Returns a concurrent {@code Collector} implementing a cascaded "group by"
* operation on input elements of type {@code T}, grouping elements
* according to a classification function, and then performing a reduction
* operation on the values associated with a given key using the specified
* downstream {@code Collector}.
*/ // ConcurrentHashMap 是一個支持並發的Map
public static <T, K>
Collector<T, ?, ConcurrentMap<K, List<T>>>
groupingByConcurrent(Function<? super T, ? extends K> classifier) {
return groupingByConcurrent(classifier, ConcurrentHashMap::new, toList());
}
public static <T, K, A, D>
Collector<T, ?, ConcurrentMap<K, D>> groupingByConcurrent(Function<? super T, ? extends K> classifier,
Collector<? super T, A, D> downstream) {
return groupingByConcurrent(classifier, ConcurrentHashMap::new, downstream);
}
public static <T, K, A, D, M extends ConcurrentMap<K, D>>
Collector<T, ?, M> groupingByConcurrent(Function<? super T, ? extends K> classifier,
Supplier<M> mapFactory,
Collector<? super T, A, D> downstream) {
Supplier<A> downstreamSupplier = downstream.supplier();
BiConsumer<A, ? super T> downstreamAccumulator = downstream.accumulator();
BinaryOperator<ConcurrentMap<K, A>> merger = Collectors.<K, A, ConcurrentMap<K, A>>mapMerger(downstream.combiner());
@SuppressWarnings("unchecked")
Supplier<ConcurrentMap<K, A>> mangledFactory = (Supplier<ConcurrentMap<K, A>>) mapFactory;
BiConsumer<ConcurrentMap<K, A>, T> accumulator;
if (downstream.characteristics().contains(Collector.Characteristics.CONCURRENT)) {
accumulator = (m, t) -> {
K key = Objects.requireNonNull(classifier.apply(t), "element cannot be mapped to a null key");
A resultContainer = m.computeIfAbsent(key, k -> downstreamSupplier.get());
downstreamAccumulator.accept(resultContainer, t);
};
}
else {
accumulator = (m, t) -> {
K key = Objects.requireNonNull(classifier.apply(t), "element cannot be mapped to a null key");
A resultContainer = m.computeIfAbsent(key, k -> downstreamSupplier.get());
synchronized (resultContainer) { // 這里有一個同步的操作。雖然是多線程操作同一容器,但是同時還是只有一個線程操作,進行了同步。
downstreamAccumulator.accept(resultContainer, t);
}
};
}
if (downstream.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)) {
return new CollectorImpl<>(mangledFactory, accumulator, merger, CH_CONCURRENT_ID);
}
else {
@SuppressWarnings("unchecked")
Function<A, A> downstreamFinisher = (Function<A, A>) downstream.finisher();
Function<ConcurrentMap<K, A>, M> finisher = intermediate -> {
intermediate.replaceAll((k, v) -> downstreamFinisher.apply(v));
@SuppressWarnings("unchecked")
M castResult = (M) intermediate;
return castResult;
};
return new CollectorImpl<>(mangledFactory, accumulator, merger, finisher, CH_CONCURRENT_NOID);
}
}
14. partitioningBy(); 分區詳解。
public static <T>
Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> predicate) {
return partitioningBy(predicate, toList());
}
public static <T, D, A>
Collector<T, ?, Map<Boolean, D>> partitioningBy(Predicate<? super T> predicate,
Collector<? super T, A, D> downstream) {
BiConsumer<A, ? super T> downstreamAccumulator = downstream.accumulator();
BiConsumer<Partition<A>, T> accumulator = (result, t) ->
downstreamAccumulator.accept(predicate.test(t) ? result.forTrue : result.forFalse, t);
BinaryOperator<A> op = downstream.combiner();
BinaryOperator<Partition<A>> merger = (left, right) ->
new Partition<>(op.apply(left.forTrue, right.forTrue),
op.apply(left.forFalse, right.forFalse));
Supplier<Partition<A>> supplier = () ->
new Partition<>(downstream.supplier().get(),
downstream.supplier().get());
if (downstream.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)) {
return new CollectorImpl<>(supplier, accumulator, merger, CH_ID);
}
else {
Function<Partition<A>, Map<Boolean, D>> finisher = par ->
new Partition<>(downstream.finisher().apply(par.forTrue),
downstream.finisher().apply(par.forFalse));
return new CollectorImpl<>(supplier, accumulator, merger, finisher, CH_NOID);
}
}
jdk的代碼,就是我們學習的范本。
講這么細的原因並不是因為要自己去寫,是為了了解內部是具體怎么實現的。調用的時候就信心非常的足。
附一個小插曲。
Java8(5)Stream流源碼詳解
節前小插曲
AutoCloseable接口: 通過一個例子 舉例自動關閉流的實現。
public interface BaseStream<T, S extends BaseStream<T, S>>
extends AutoCloseable{} // BaseStream 繼承了這個接口。 Stream繼承了Stream
public class AutoCloseableTest implements AutoCloseable {
public void dosomething() {
System.out.println(" do something ");
}
@Override
public void close() throws Exception {
System.out.println(" close invoked ");
}
public static void main(String[] args) throws Exception {
try ( AutoCloseableTest autoCloseableTest = new AutoCloseableTest()){
autoCloseableTest.dosomething();
}
}
}
運行結果如下: 自動調用了關閉流的方法
Stream
/**
* A sequence of elements supporting sequential and parallel aggregate
* operations. The following example illustrates an aggregate operation using
* {@link Stream} and {@link IntStream}:
*
* <pre>{@code // 舉例:
* int sum = widgets.stream()
* .filter(w -> w.getColor() == RED)
* .mapToInt(w -> w.getWeight())
* .sum();
* }</pre>
*
* In this example, {@code widgets} is a {@code Collection<Widget>}. We create
* a stream of {@code Widget} objects via {@link Collection#stream Collection.stream()},
* filter it to produce a stream containing only the red widgets, and then
* transform it into a stream of {@code int} values representing the weight of
* each red widget. Then this stream is summed to produce a total weight.
*
* <p>In addition to {@code Stream}, which is a stream of object references,
* there are primitive specializations for {@link IntStream}, {@link LongStream},
* and {@link DoubleStream}, all of which are referred to as "streams" and
* conform to the characteristics and restrictions described here.
jdk提供了平行的 特化的流。
*
* <p>To perform a computation, stream
* <a href="package-summary.html#StreamOps">operations</a> are composed into a
* <em>stream pipeline</em>. A stream pipeline consists of a source (which
* might be an array, a collection, a generator function, an I/O channel,
* etc), zero or more <em>intermediate operations</em> (which transform a
* stream into another stream, such as {@link Stream#filter(Predicate)}), and a
* <em>terminal operation</em> (which produces a result or side-effect, such
* as {@link Stream#count()} or {@link Stream#forEach(Consumer)}).
* Streams are lazy; computation on the source data is only performed when the
* terminal operation is initiated, and source elements are consumed only
* as needed.
為了執行計算,流會被執行到一個流管道當中。
一個流管道包含了:
一個源。(數字來的地方)
0個或多個中間操作(將一個stream轉換成另外一個Stream)。
一個終止操作(會生成一個結果,或者是一個副作用(求和,遍歷))。
流是延遲的,只有當終止操作被發起的時候,才會執行中間操作。
* <p>Collections and streams, while bearing some superficial similarities,
* have different goals. Collections are primarily concerned with the efficient
* management of, and access to, their elements. By contrast, streams do not
* provide a means to directly access or manipulate their elements, and are
* instead concerned with declaratively describing their source and the
* computational operations which will be performed in aggregate on that source.
* However, if the provided stream operations do not offer the desired
* functionality, the {@link #iterator()} and {@link #spliterator()} operations
* can be used to perform a controlled traversal.
集合和流雖然有一些相似性,但是他們的差異是不同的。
集合是為了高效對於元素的管理和訪問。流並不會提供方式去直接操作流里的元素。(集合關注的是數據的管理,流關注的是元素內容的計算)
如果流操作並沒有提供我們需要的功能,那么我們可以使用傳統的iterator or spliterator去執行操作。
* <p>A stream pipeline, like the "widgets" example above, can be viewed as
* a <em>query</em> on the stream source. Unless the source was explicitly
* designed for concurrent modification (such as a {@link ConcurrentHashMap}),
* unpredictable or erroneous behavior may result from modifying the stream
* source while it is being queried.
一個流管道,可以看做是對流源的查詢,除非這個流被顯示的設計成可以並發修改的。否則會拋出異常。
(如一個線程對流進行修改,另一個對流進行查詢)
* <p>Most stream operations accept parameters that describe user-specified
* behavior, such as the lambda expression {@code w -> w.getWeight()} passed to
* {@code mapToInt} in the example above. To preserve correct behavior,
* these <em>behavioral parameters</em>:
//為了能滿足結果,需滿足下邊的條件。
* <ul>
* <li>must be <a href="package-summary.html#NonInterference">non-interfering</a>
* (they do not modify the stream source); and</li>
* <li>in most cases must be <a href="package-summary.html#Statelessness">stateless</a>
* (their result should not depend on any state that might change during execution
* of the stream pipeline).</li>
* </ul>
行為上的參數,大多是無狀態的。
* <p>Such parameters are always instances of a
* <a href="../function/package-summary.html">functional interface</a> such
* as {@link java.util.function.Function}, and are often lambda expressions or
* method references. Unless otherwise specified these parameters must be
* <em>non-null</em>.
無一例外的。這種參數總是函數式接口的形式。也就是lambda表達式。除非特別指定,這些參數必須是非空的。
* <p>A stream should be operated on (invoking an intermediate or terminal stream
* operation) only once. This rules out, for example, "forked" streams, where
* the same source feeds two or more pipelines, or multiple traversals of the
* same stream. A stream implementation may throw {@link IllegalStateException}
* if it detects that the stream is being reused. However, since some stream
* operations may return their receiver rather than a new stream object, it may
* not be possible to detect reuse in all cases.
一個流只能被使用一次。對相同的流進行多次操作,需要創建多個流管道。
* <p>Streams have a {@link #close()} method and implement {@link AutoCloseable},
* but nearly all stream instances do not actually need to be closed after use.
* Generally, only streams whose source is an IO channel (such as those returned
* by {@link Files#lines(Path, Charset)}) will require closing. Most streams
* are backed by collections, arrays, or generating functions, which require no
* special resource management. (If a stream does require closing, it can be
* declared as a resource in a {@code try}-with-resources statement.)
流擁有一個closed方法,實現了AutoCloseable,在他的父類里。 最上面以舉例實現。
但是一個流 除了是I/O流(因為持有句柄等資源)才需要被關閉外,是不需要被關閉的。
大多數的流底層是集合、數組或者是生成器函數。 他們並不需要特別的資源管理。如果需要被關閉,可以用try()操作。
* <p>Stream pipelines may execute either sequentially or in
* <a href="package-summary.html#Parallelism">parallel</a>. This
* execution mode is a property of the stream. Streams are created
* with an initial choice of sequential or parallel execution. (For example,
* {@link Collection#stream() Collection.stream()} creates a sequential stream,
* and {@link Collection#parallelStream() Collection.parallelStream()} creates
* a parallel one.) This choice of execution mode may be modified by the
* {@link #sequential()} or {@link #parallel()} methods, and may be queried with
* the {@link #isParallel()} method.
流管道可以被串行或者並行操作。這種模式只是一個屬性而已。 初始化的時候會進行一個選擇。
比如說 stream() 是串行流。parallelStream()是並行流。
還可以通過sequential()or parallel() 來進行修改。 以最后一個被調用的方法為准。
也可以用isParallel()來進行查詢流是否是並行流。
* @param <T> the type of the stream elements
* @since 1.8
* @see IntStream
* @see LongStream
* @see DoubleStream
* @see <a href="package-summary.html">java.util.stream</a>
*/
public interface Stream<T> extends BaseStream<T, Stream<T>> {
// 具體舉例, 源碼中有例子
Stream<T> filter(Predicate<? super T> predicate); // 過濾
<R> Stream<R> map(Function<? super T, ? extends R> mapper); //映射
IntStream mapToInt(ToIntFunction<? super T> mapper);
LongStream mapToLong(ToLongFunction<? super T> mapper);
DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper);
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper); //壓平
IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper);
LongStream flatMapToLong(Function<? super T, ? extends LongStream> mapper);
DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> mapper);、
Stream<T> distinct();// 去重
Stream<T> sorted(); //排序
Stream<T> sorted(Comparator<? super T> comparator);
Stream<T> peek(Consumer<? super T> action);
Stream<T> limit(long maxSize); // 截斷
void forEach(Consumer<? super T> action); // 遍歷
void forEachOrdered(Consumer<? super T> action); // 遍歷時執行操作
Object[] toArray(); // 轉數組
T reduce(T identity, BinaryOperator<T> accumulator); // 匯聚, 返回一個匯聚的結果
<R> R collect(Supplier<R> supplier,
BiConsumer<R, ? super T> accumulator,
BiConsumer<R, R> combiner); // 收集器
。。。
}
自行參考父接口中的方法;
Stream中具體方法的詳解
分割迭代器:
/**
* Returns a spliterator for the elements of this stream.
*
* <p>This is a <a href="package-summary.html#StreamOps">terminal
* operation</a>.
*
* @return the element spliterator for this stream
*/
Spliterator<T> spliterator();
Java8(6)spliterator及baseStream 源碼講解
在公司的學習筆記。
baseStream 源碼講解
BaseStream 是所有流的父類 。
/**
* Base interface for streams, which are sequences of elements supporting
* sequential and parallel aggregate operations. The following example
* illustrates an aggregate operation using the stream types {@link Stream}
* and {@link IntStream}, computing the sum of the weights of the red widgets:
*
* <pre>{@code
* int sum = widgets.stream()
* .filter(w -> w.getColor() == RED)
* .mapToInt(w -> w.getWeight())
* .sum();
* }</pre>
*
* See the class documentation for {@link Stream} and the package documentation
* for <a href="package-summary.html">java.util.stream</a> for additional
* specification of streams, stream operations, stream pipelines, and
* parallelism, which governs the behavior of all stream types.
*
* @param <T> the type of the stream elements
* @param <S> the type of of the stream implementing {@code BaseStream}
* @since 1.8
* @see Stream
* @see IntStream
* @see LongStream
* @see DoubleStream
* @see <a href="package-summary.html">java.util.stream</a>
*/
public interface BaseStream<T, S extends BaseStream<T, S>> extends AutoCloseable
public interface Stream<T> extends BaseStream<T, Stream<T>>
BaseStream(){
Iterator<T> iterator(); 迭代器
Spliterator<T> spliterator(); 分割迭代器 。 這是一個流的終止操作。
boolean isParallel(); 是否是並行。
S sequential(); // 返回一個等價的串行流。 返回S是一個新的流對象
S parallel(); //返回一個並行流。
S unordered(); // 返回一個無序的流。
S onClose(Runnable closeHandler); //當前流.onClose、 當close調用時,調用此方法。
void close(); // 關閉流
}
關閉處理器的舉例
/**
* Returns an equivalent stream with an additional close handler. Close
* handlers are run when the {@link #close()} method
* is called on the stream, and are executed in the order they were
* added. All close handlers are run, even if earlier close handlers throw
* exceptions. If any close handler throws an exception, the first
* exception thrown will be relayed to the caller of {@code close()}, with
* any remaining exceptions added to that exception as suppressed exceptions
* (unless one of the remaining exceptions is the same exception as the
* first exception, since an exception cannot suppress itself.) May
* return itself.
*
* <p>This is an <a href="package-summary.html#StreamOps">intermediate
* operation</a>.
*
* @param closeHandler A task to execute when the stream is closed
* @return a stream with a handler that is run if the stream is closed
*/
S onClose(Runnable closeHandler);
public static void main(String[] args) {
List<String> list = Arrays.asList("hello","world");
NullPointerException nullPointerException = new NullPointerException("myexception");
try (Stream<String> stream = list.stream()){
stream.onClose(()->{
System.out.println("aaa");
// throw new NullPointerException("first");
throw nullPointerException;
}).onClose(()->{
System.out.println("aaa");
throw nullPointerException;
}).forEach(System.out::println);
}
// 出現異常會被壓制,
// 如果是同一個異常對象,只會打印一次異常。 如果是多個異常對象。都會被打印。
}
javadoc 中的介紹比任何資料都詳細。
Stream 源碼分析。
stream();
/**
* Returns a sequential {@code Stream} with this collection as its source.
返回一個串行流,把這個集合當做源
* <p>This method should be overridden when the {@link #spliterator()}
* method cannot return a spliterator that is {@code IMMUTABLE},
* {@code CONCURRENT}, or <em>late-binding</em>. (See {@link #spliterator()}
* for details.)
當不能返回 三種方法 中的一個時,這個方法應該被重寫。
* @implSpec
* The default implementation creates a sequential {@code Stream} from the
* collection's {@code Spliterator}.
默認會從集合中創建一個串行流。 返回
* @return a sequential {@code Stream} over the elements in this collection
* @since 1.8
*/
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
spliterator(); 分割迭代器
/**
* Creates a {@link Spliterator} over the elements in this collection.
*
* Implementations should document characteristic values reported by the
* spliterator. Such characteristic values are not required to be reported
* if the spliterator reports {@link Spliterator#SIZED} and this collection
* contains no elements.
* <p>The default implementation should be overridden by subclasses that
* can return a more efficient spliterator. In order to
* preserve expected laziness behavior for the {@link #stream()} and
* {@link #parallelStream()}} methods, spliterators should either have the
* characteristic of {@code IMMUTABLE} or {@code CONCURRENT}, or be
* <em><a href="Spliterator.html#binding">late-binding</a></em>.
默認的子類應該被重寫。為了保留parallelStream 和 stream的延遲行為。特性需要滿足IMMUTABLE 或者CONCURRENT
* If none of these is practical, the overriding class should describe the
* spliterator's documented policy of binding and structural interference,
* and should override the {@link #stream()} and {@link #parallelStream()}
* methods to create streams using a {@code Supplier} of the spliterator,
* as in:
* <pre>{@code
* Stream<E> s = StreamSupport.stream(() -> spliterator(), spliteratorCharacteristics)
* }</pre>
為什么叫分割迭代器。先分割,在迭代。
如果不能滿足上述的要求,則重寫的時候應該滿足上述的需求、
* <p>These requirements ensure that streams produced by the
* {@link #stream()} and {@link #parallelStream()} methods will reflect the
* contents of the collection as of initiation of the terminal stream
* operation.
這些確保了流會返回的內容。
* @implSpec
* The default implementation creates a
* <em><a href="Spliterator.html#binding">late-binding</a></em> spliterator
* from the collections's {@code Iterator}. The spliterator inherits the
* <em>fail-fast</em> properties of the collection's iterator.
* <p>
* The created {@code Spliterator} reports {@link Spliterator#SIZED}.
默認會從集合的迭代器中創建出一個延遲的分割迭代器。 默認的迭代器 會有默認大小的迭代器。
* @implNote
* The created {@code Spliterator} additionally reports
* {@link Spliterator#SUBSIZED}.
*
* <p>If a spliterator covers no elements then the reporting of additional
* characteristic values, beyond that of {@code SIZED} and {@code SUBSIZED},
* does not aid clients to control, specialize or simplify computation.
* However, this does enable shared use of an immutable and empty
* spliterator instance (see {@link Spliterators#emptySpliterator()}) for
* empty collections, and enables clients to determine if such a spliterator
* covers no elements.
如果分割迭代器不包含任何元素。 其他的屬性對客戶端是沒有任何幫助的。 然而會促進分割迭代器共享的作用。
* @return a {@code Spliterator} over the elements in this collection
* @since 1.8
*/
@Override
default Spliterator<E> spliterator() {
return Spliterators.spliterator(this, 0);
}
Spliterator javadoc
/**
* An object for traversing and partitioning elements of a source. The source
* of elements covered by a Spliterator could be, for example, an array, a
* {@link Collection}, an IO channel, or a generator function.
* <p>A Spliterator may traverse elements individually ({@link
* #tryAdvance tryAdvance()}) or sequentially in bulk
* ({@link #forEachRemaining forEachRemaining()}).
一個一個去遍歷 tryAdvance() 或者 成塊的遍歷forEachRemaining()
*
* <p>A Spliterator may also partition off some of its elements (using
* {@link #trySplit}) as another Spliterator, to be used in
* possibly-parallel operations. Operations using a Spliterator that
* cannot split, or does so in a highly imbalanced or inefficient
* manner, are unlikely to benefit from parallelism. Traversal
* and splitting exhaust elements; each Spliterator is useful for only a single
* bulk computation.
Spliterator 可以對元素進行分區。 分成新的Spliterator。 並且以並行的操作來實行。
如果不能分割這些操作,則不能通過並行操作受益。
遍歷和分割都會對那一小塊是有用的、
* <p>A Spliterator also reports a set of {@link #characteristics()} of its
* structure, source, and elements from among {@link #ORDERED},
* {@link #DISTINCT}, {@link #SORTED}, {@link #SIZED}, {@link #NONNULL},
* {@link #IMMUTABLE}, {@link #CONCURRENT}, and {@link #SUBSIZED}. These may
* be employed by Spliterator clients to control, specialize or simplify
* computation. For example, a Spliterator for a {@link Collection} would
* report {@code SIZED}, a Spliterator for a {@link Set} would report
* {@code DISTINCT}, and a Spliterator for a {@link SortedSet} would also
* report {@code SORTED}. Characteristics are reported as a simple unioned bit
* set.
特性值:ORDERED 有序的, DISTINCT 不同的,SORTED 帶排序的,SIZED 確定大小的,
NONNULL 非空的, IMMUTABLE, CONCURRENT ,SUBSIZED
這些特性可以在客戶端使用,用來簡化計算。是以位操作的形式來表示的。 collector中是以枚舉形式來表示的。
* Some characteristics additionally constrain method behavior; for example if
* {@code ORDERED}, traversal methods must conform to their documented ordering.
* New characteristics may be defined in the future, so implementors should not
* assign meanings to unlisted values.
未來可能會定義一下新的特性。 實現者不應該賦予新的含義。
* <p><a name="binding">A Spliterator that does not report {@code IMMUTABLE} or
* {@code CONCURRENT} is expected to have a documented policy concerning:
* when the spliterator <em>binds</em> to the element source; and detection of
* structural interference of the element source detected after binding.</a> A
* <em>late-binding</em> Spliterator binds to the source of elements at the
* point of first traversal, first split, or first query for estimated size,
* rather than at the time the Spliterator is created. A Spliterator that is
* not <em>late-binding</em> binds to the source of elements at the point of
* construction or first invocation of any method. Modifications made to the
* source prior to binding are reflected when the Spliterator is traversed.
* After binding a Spliterator should, on a best-effort basis, throw
* {@link ConcurrentModificationException} if structural interference is
* detected. Spliterators that do this are called <em>fail-fast</em>. The
* bulk traversal method ({@link #forEachRemaining forEachRemaining()}) of a
* Spliterator may optimize traversal and check for structural interference
* after all elements have been traversed, rather than checking per-element and
* failing immediately.
當Spliterator 綁定到源上時 , 要考慮 IMMUTABLE 和 CONCURRENT 。
延遲迭代器會在第一次遍歷或者分割或者查詢大小的時候綁定在源上邊。而不是在創建的時候就被綁定在源上了。
非延遲迭代器是在創建的時候就被綁定在源上了。
在迭代器綁定后對源進行了修改。迭代器就能反應出來拋出異常ConcurrentModificationException。
forEachRemaining()會優化遍歷,是在所有元素都被操作之后進行操作,而不是一個元素一個元素的檢測、
* <p>Spliterators can provide an estimate of the number of remaining elements
* via the {@link #estimateSize} method. Ideally, as reflected in characteristic
* {@link #SIZED}, this value corresponds exactly to the number of elements
* that would be encountered in a successful traversal. However, even when not
* exactly known, an estimated value value may still be useful to operations
* being performed on the source, such as helping to determine whether it is
* preferable to split further or traverse the remaining elements sequentially.
如果 特性值 SIZED 。 那么將要遍歷的數量是確定的。
如果不包含SIZED,一個估算的值對於源的操作也是有幫助的。
* <p>Despite their obvious utility in parallel algorithms, spliterators are not
* expected to be thread-safe; instead, implementations of parallel algorithms
* using spliterators should ensure that the spliterator is only used by one
* thread at a time. This is generally easy to attain via <em>serial
* thread-confinement</em>, which often is a natural consequence of typical
* parallel algorithms that work by recursive decomposition. A thread calling
* {@link #trySplit()} may hand over the returned Spliterator to another thread,
* which in turn may traverse or further split that Spliterator. The behaviour
* of splitting and traversal is undefined if two or more threads operate
* concurrently on the same spliterator. If the original thread hands a
* spliterator off to another thread for processing, it is best if that handoff
* occurs before any elements are consumed with {@link #tryAdvance(Consumer)
* tryAdvance()}, as certain guarantees (such as the accuracy of
* {@link #estimateSize()} for {@code SIZED} spliterators) are only valid before
* traversal has begun.
分割迭代器並不確保是線程安全的。相反 應該確保分割迭代器一次被一個線程操作、
可以通過遞歸的方式來實現。
* <p>Primitive subtype specializations of {@code Spliterator} are provided for
* {@link OfInt int}, {@link OfLong long}, and {@link OfDouble double} values.
* The subtype default implementations of
* {@link Spliterator#tryAdvance(java.util.function.Consumer)}
* and {@link Spliterator#forEachRemaining(java.util.function.Consumer)} box
* primitive values to instances of their corresponding wrapper class. Such
* boxing may undermine any performance advantages gained by using the primitive
* specializations. To avoid boxing, the corresponding primitive-based methods
* should be used. For example,
* {@link Spliterator.OfInt#tryAdvance(java.util.function.IntConsumer)}
* and {@link Spliterator.OfInt#forEachRemaining(java.util.function.IntConsumer)}
* should be used in preference to
* {@link Spliterator.OfInt#tryAdvance(java.util.function.Consumer)} and
* {@link Spliterator.OfInt#forEachRemaining(java.util.function.Consumer)}.
* Traversal of primitive values using boxing-based methods
* {@link #tryAdvance tryAdvance()} and
* {@link #forEachRemaining(java.util.function.Consumer) forEachRemaining()}
* does not affect the order in which the values, transformed to boxed values,
* are encountered.
為了避免重復的裝箱和拆箱,我們應該使用巨化的方法。 減少使用通用的方法。
*
* @apiNote
* <p>Spliterators, like {@code Iterator}s, are for traversing the elements of
* a source. The {@code Spliterator} API was designed to support efficient
* parallel traversal in addition to sequential traversal, by supporting
* decomposition as well as single-element iteration. In addition, the
* protocol for accessing elements via a Spliterator is designed to impose
* smaller per-element overhead than {@code Iterator}, and to avoid the inherent
* race involved in having separate methods for {@code hasNext()} and
* {@code next()}.
分割迭代器 就像迭代器一樣。 用來遍歷源當中的元素的。
Spliterator也支持並行的操作。方式是通過解耦,分解,單元素的遍歷迭代。
Spliterator相比於Iterator來說,成本更低。tryAdvance()本質上也避免了hasNext()和next() 的資源上的競爭。
* <p>For mutable sources, arbitrary and non-deterministic behavior may occur if
* the source is structurally interfered with (elements added, replaced, or
* removed) between the time that the Spliterator binds to its data source and
* the end of traversal. For example, such interference will produce arbitrary,
* non-deterministic results when using the {@code java.util.stream} framework.
對於可變源來說,可能會出現問題
* <p>Structural interference of a source can be managed in the following ways
* (in approximate order of decreasing desirability):
* <ul>
* <li>The source cannot be structurally interfered with.
* <br>For example, an instance of
* {@link java.util.concurrent.CopyOnWriteArrayList} is an immutable source.
* A Spliterator created from the source reports a characteristic of
* {@code IMMUTABLE}.</li>
CopyOnWriteArrayList 適合於 讀多寫少的場景。 他是一個不可變的源。會返回一個特性值IMMUTABLE
* <li>The source manages concurrent modifications.
* <br>For example, a key set of a {@link java.util.concurrent.ConcurrentHashMap}
* is a concurrent source. A Spliterator created from the source reports a
* characteristic of {@code CONCURRENT}.</li>
創建並發源、 特性值 CONCURRENT
* <li>The mutable source provides a late-binding and fail-fast Spliterator.
* <br>Late binding narrows the window during which interference can affect
* the calculation; fail-fast detects, on a best-effort basis, that structural
* interference has occurred after traversal has commenced and throws
* {@link ConcurrentModificationException}. For example, {@link ArrayList},
* and many other non-concurrent {@code Collection} classes in the JDK, provide
* a late-binding, fail-fast spliterator.</li>
* <li>The mutable source provides a non-late-binding but fail-fast Spliterator.
* <br>The source increases the likelihood of throwing
* {@code ConcurrentModificationException} since the window of potential
* interference is larger.</li>
* <li>The mutable source provides a late-binding and non-fail-fast Spliterator.
* <br>The source risks arbitrary, non-deterministic behavior after traversal
* has commenced since interference is not detected.
* </li>
* <li>The mutable source provides a non-late-binding and non-fail-fast
* Spliterator.
* <br>The source increases the risk of arbitrary, non-deterministic behavior
* since non-detected interference may occur after construction.
* </li>
* </ul>
*
// 串行的例子:
* <p><b>Example.</b> Here is a class (not a very useful one, except
* for illustration) that maintains an array in which the actual data
* are held in even locations, and unrelated tag data are held in odd
* locations. Its Spliterator ignores the tags.
*
* <pre> {@code
* class TaggedArray<T> {
* private final Object[] elements; // immutable after construction
* TaggedArray(T[] data, Object[] tags) {
* int size = data.length;
* if (tags.length != size) throw new IllegalArgumentException();
* this.elements = new Object[2 * size];
* for (int i = 0, j = 0; i < size; ++i) {
* elements[j++] = data[i];
* elements[j++] = tags[i];
* }
* }
*
* public Spliterator<T> spliterator() {
* return new TaggedArraySpliterator<>(elements, 0, elements.length);
* }
*
* static class TaggedArraySpliterator<T> implements Spliterator<T> {
* private final Object[] array;
* private int origin; // current index, advanced on split or traversal
* private final int fence; // one past the greatest index
*
* TaggedArraySpliterator(Object[] array, int origin, int fence) {
* this.array = array; this.origin = origin; this.fence = fence;
* }
*
* public void forEachRemaining(Consumer<? super T> action) {
* for (; origin < fence; origin += 2)
* action.accept((T) array[origin]);
* }
*
// 讓這個迭代器往前走
* public boolean tryAdvance(Consumer<? super T> action) {
* if (origin < fence) {
* action.accept((T) array[origin]);
* origin += 2;
* return true;
* }
* else // cannot advance
* return false;
* }
*
// 嘗試進行分割, 盡量均勻的分割成兩半. 不成功返回null
* public Spliterator<T> trySplit() {
* int lo = origin; // divide range in half
* int mid = ((lo + fence) >>> 1) & ~1; // force midpoint to be even
* if (lo < mid) { // split out left half
* origin = mid; // reset this Spliterator's origin
* return new TaggedArraySpliterator<>(array, lo, mid);
* }
* else // too small to split
* return null;
* }
*
* public long estimateSize() {
* return (long)((fence - origin) / 2);
* }
*
* public int characteristics() {
* return ORDERED | SIZED | IMMUTABLE | SUBSIZED;
* }
* }
* }}</pre>
*
// 並行的例子
* <p>As an example how a parallel computation framework, such as the
* {@code java.util.stream} package, would use Spliterator in a parallel
* computation, here is one way to implement an associated parallel forEach,
* that illustrates the primary usage idiom of splitting off subtasks until
* the estimated amount of work is small enough to perform
* sequentially. Here we assume that the order of processing across
* subtasks doesn't matter; different (forked) tasks may further split
* and process elements concurrently in undetermined order. This
* example uses a {@link java.util.concurrent.CountedCompleter};
* similar usages apply to other parallel task constructions.
*
* <pre>{@code
* static <T> void parEach(TaggedArray<T> a, Consumer<T> action) {
* Spliterator<T> s = a.spliterator();
* long targetBatchSize = s.estimateSize() / (ForkJoinPool.getCommonPoolParallelism() * 8);
* new ParEach(null, s, action, targetBatchSize).invoke();
* }
*
* static class ParEach<T> extends CountedCompleter<Void> {
* final Spliterator<T> spliterator;
* final Consumer<T> action;
* final long targetBatchSize;
*
* ParEach(ParEach<T> parent, Spliterator<T> spliterator,
* Consumer<T> action, long targetBatchSize) {
* super(parent);
* this.spliterator = spliterator; this.action = action;
* this.targetBatchSize = targetBatchSize;
* }
*
* public void compute() {
* Spliterator<T> sub;
* while (spliterator.estimateSize() > targetBatchSize &&
* (sub = spliterator.trySplit()) != null) {
* addToPendingCount(1);
* new ParEach<>(this, sub, action, targetBatchSize).fork();
* }
* spliterator.forEachRemaining(action);
* propagateCompletion();
* }
* }}</pre>
*
* @implNote
* If the boolean system property {@code org.openjdk.java.util.stream.tripwire}
* is set to {@code true} then diagnostic warnings are reported if boxing of
* primitive values occur when operating on primitive subtype specializations.
* 如果 {@code org.openjdk.java.util.stream.tripwire} 被設置成true, 則會給出警告。
* @param <T> the type of elements returned by this Spliterator
*
* @see Collection
* @since 1.8
*/
public interface Spliterator<T> {
// 。。。 下方列舉幾個
}
tryAdvance();
/**
* If a remaining element exists, performs the given action on it,
* returning {@code true}; else returns {@code false}. If this
* Spliterator is {@link #ORDERED} the action is performed on the
* next element in encounter order. Exceptions thrown by the
* action are relayed to the caller.
*
* @param action The action
* @return {@code false} if no remaining elements existed
* upon entry to this method, else {@code true}.
* @throws NullPointerException if the specified action is null
*/
//嘗試的去前進。如果有下一個元素,則進行動作。
boolean tryAdvance(Consumer<? super T> action);
forEachRemaining();
/**
* Performs the given action for each remaining element, sequentially in
* the current thread, until all elements have been processed or the action
* throws an exception. If this Spliterator is {@link #ORDERED}, actions
* are performed in encounter order. Exceptions thrown by the action
* are relayed to the caller.
*
* @implSpec
* The default implementation repeatedly invokes {@link #tryAdvance} until
* it returns {@code false}. It should be overridden whenever possible.
*
* @param action The action
* @throws NullPointerException if the specified action is null
*/
// 針對於剩余的元素進行操作。
default void forEachRemaining(Consumer<? super T> action) {
do { } while (tryAdvance(action));
}
trySplit();
/**
* If this spliterator can be partitioned, returns a Spliterator
* covering elements, that will, upon return from this method, not
* be covered by this Spliterator.
* 如果這個分割迭代器能被分割,則返回一個新的被分割出來的Spliterator對象。
不會影響當前的spliterator
* <p>If this Spliterator is {@link #ORDERED}, the returned Spliterator
* must cover a strict prefix of the elements.
*如果Spliterator是有序的,則返回的也應該是有序的Spliterator
* <p>Unless this Spliterator covers an infinite number of elements,
* repeated calls to {@code trySplit()} must eventually return {@code null}.
除非Spliterator 返回的是無窮的元素,其余的最終返回一個null . 表示不能再繼續分割了。
* Upon non-null return:
如果返回不為Null的話,
* <ul>
* <li>the value reported for {@code estimateSize()} before splitting,
* must, after splitting, be greater than or equal to {@code estimateSize()}
* for this and the returned Spliterator; and</li>
分割前的estimateSize>= 返回的estimateSize
* <li>if this Spliterator is {@code SUBSIZED}, then {@code estimateSize()}
* for this spliterator before splitting must be equal to the sum of
* {@code estimateSize()} for this and the returned Spliterator after
* splitting.</li>
如果大小是固定的。則分割后的 estimateSize 的總和 等於分割前的 estimateSize
* </ul>
*
* <p>This method may return {@code null} for any reason,
* including emptiness, inability to split after traversal has
* commenced, data structure constraints, and efficiency
* considerations.
*
* @apiNote
* An ideal {@code trySplit} method efficiently (without
* traversal) divides its elements exactly in half, allowing
* balanced parallel computation. Many departures from this ideal
* remain highly effective; for example, only approximately
* splitting an approximately balanced tree, or for a tree in
* which leaf nodes may contain either one or two elements,
* failing to further split these nodes. However, large
* deviations in balance and/or overly inefficient {@code
* trySplit} mechanics typically result in poor parallel
* performance.
*理想情況下,是從中間分割的,允許並行計算。很多情況下不滿足這種的、
然而沒有效率的分割,會降低效率
* @return a {@code Spliterator} covering some portion of the
* elements, or {@code null} if this spliterator cannot be split
*/
//嘗試分割。
Spliterator<T> trySplit();
estimateSize();
/**
* Returns an estimate of the number of elements that would be
* encountered by a {@link #forEachRemaining} traversal, or returns {@link
* Long#MAX_VALUE} if infinite, unknown, or too expensive to compute.
*
* <p>If this Spliterator is {@link #SIZED} and has not yet been partially
* traversed or split, or this Spliterator is {@link #SUBSIZED} and has
* not yet been partially traversed, this estimate must be an accurate
* count of elements that would be encountered by a complete traversal.
* Otherwise, this estimate may be arbitrarily inaccurate, but must decrease
* as specified across invocations of {@link #trySplit}.
* 分的越少,estimateSize要比原來的個數要小。
* @apiNote
* Even an inexact estimate is often useful and inexpensive to compute.
* For example, a sub-spliterator of an approximately balanced binary tree
* may return a value that estimates the number of elements to be half of
* that of its parent; if the root Spliterator does not maintain an
* accurate count, it could estimate size to be the power of two
* corresponding to its maximum depth.
*一個不精算的數量也是有用的。
* @return the estimated size, or {@code Long.MAX_VALUE} if infinite,
* unknown, or too expensive to compute.
*/
//估算大小
long estimateSize();
characteristics();
/**
* Returns a set of characteristics of this Spliterator and its
* elements. The result is represented as ORed values from {@link
* #ORDERED}, {@link #DISTINCT}, {@link #SORTED}, {@link #SIZED},
* {@link #NONNULL}, {@link #IMMUTABLE}, {@link #CONCURRENT},
* {@link #SUBSIZED}. Repeated calls to {@code characteristics()} on
* a given spliterator, prior to or in-between calls to {@code trySplit},
* should always return the same result.
一個特性值的集合。
重復的調用characteristics 在spliterator之前或者當中,會返回相同的結果。
* <p>If a Spliterator reports an inconsistent set of
* characteristics (either those returned from a single invocation
* or across multiple invocations), no guarantees can be made
* about any computation using this Spliterator.
如果返回了一個不一致的特性值的集合。結果是不被保障的。
* @apiNote The characteristics of a given spliterator before splitting
* may differ from the characteristics after splitting. For specific
* examples see the characteristic values {@link #SIZED}, {@link #SUBSIZED}
* and {@link #CONCURRENT}.
*
* @return a representation of characteristics
*/
//特性值。
int characteristics();
hasCharacteristics();
/**
* Returns {@code true} if this Spliterator's {@link
* #characteristics} contain all of the given characteristics.
*
* @implSpec
* The default implementation returns true if the corresponding bits
* of the given characteristics are set.
* 默認的話,包含 會返回true
* @param characteristics the characteristics to check for
* @return {@code true} if all the specified characteristics are present,
* else {@code false}
*/
// 判斷是否包含給定的特性值。
default boolean hasCharacteristics(int characteristics) {
return (characteristics() & characteristics) == characteristics;
}
getComparator();
/**
* If this Spliterator's source is {@link #SORTED} by a {@link Comparator},
* returns that {@code Comparator}. If the source is {@code SORTED} in
* {@linkplain Comparable natural order}, returns {@code null}. Otherwise,
* if the source is not {@code SORTED}, throws {@link IllegalStateException}.
*
* @implSpec
* The default implementation always throws {@link IllegalStateException}.
*
* @return a Comparator, or {@code null} if the elements are sorted in the
* natural order.
* @throws IllegalStateException if the spliterator does not report
* a characteristic of {@code SORTED}.
*/
//有序的話 返回一個Null。 其他情況 拋異常
default Comparator<? super T> getComparator() {
throw new IllegalStateException();
}
IMMUTABLE
/**
* Characteristic value signifying that the element source cannot be
* structurally modified; that is, elements cannot be added, replaced, or
* removed, so such changes cannot occur during traversal. A Spliterator
* that does not report {@code IMMUTABLE} or {@code CONCURRENT} is expected
* to have a documented policy (for example throwing
* {@link ConcurrentModificationException}) concerning structural
* interference detected during traversal.
*/
public static final int IMMUTABLE = 0x00000400; // 不能被修改的
CONCURRENT
/**
* Characteristic value signifying that the element source may be safely
* concurrently modified (allowing additions, replacements, and/or removals)
* by multiple threads without external synchronization. If so, the
* Spliterator is expected to have a documented policy concerning the impact
* of modifications during traversal.
*
* <p>A top-level Spliterator should not report both {@code CONCURRENT} and
* {@code SIZED}, since the finite size, if known, may change if the source
* is concurrently modified during traversal. Such a Spliterator is
* inconsistent and no guarantees can be made about any computation using
* that Spliterator. Sub-spliterators may report {@code SIZED} if the
* sub-split size is known and additions or removals to the source are not
* reflected when traversing.
*
* @apiNote Most concurrent collections maintain a consistency policy
* guaranteeing accuracy with respect to elements present at the point of
* Spliterator construction, but possibly not reflecting subsequent
* additions or removals.
*/
public static final int CONCURRENT = 0x00001000;
OfPrimitive
/**
* A Spliterator specialized for primitive values.
*
* @param <T> the type of elements returned by this Spliterator. The
* type must be a wrapper type for a primitive type, such as {@code Integer}
* for the primitive {@code int} type.
* @param <T_CONS> the type of primitive consumer. The type must be a
* primitive specialization of {@link java.util.function.Consumer} for
* {@code T}, such as {@link java.util.function.IntConsumer} for
* {@code Integer}.
* @param <T_SPLITR> the type of primitive Spliterator. The type must be
* a primitive specialization of Spliterator for {@code T}, such as
* {@link Spliterator.OfInt} for {@code Integer}.
*
* @see Spliterator.OfInt
* @see Spliterator.OfLong
* @see Spliterator.OfDouble
* @since 1.8
*/
public interface OfPrimitive<T, T_CONS, T_SPLITR extends Spliterator.OfPrimitive<T, T_CONS, T_SPLITR>>
extends Spliterator<T> {
@Override
T_SPLITR trySplit();
/**
* If a remaining element exists, performs the given action on it,
* returning {@code true}; else returns {@code false}. If this
* Spliterator is {@link #ORDERED} the action is performed on the
* next element in encounter order. Exceptions thrown by the
* action are relayed to the caller.
*
* @param action The action
* @return {@code false} if no remaining elements existed
* upon entry to this method, else {@code true}.
* @throws NullPointerException if the specified action is null
*/
@SuppressWarnings("overloads")
boolean tryAdvance(T_CONS action);
/**
* Performs the given action for each remaining element, sequentially in
* the current thread, until all elements have been processed or the
* action throws an exception. If this Spliterator is {@link #ORDERED},
* actions are performed in encounter order. Exceptions thrown by the
* action are relayed to the caller.
*
* @implSpec
* The default implementation repeatedly invokes {@link #tryAdvance}
* until it returns {@code false}. It should be overridden whenever
* possible.
*
* @param action The action
* @throws NullPointerException if the specified action is null
*/
@SuppressWarnings("overloads")
default void forEachRemaining(T_CONS action) {
do { } while (tryAdvance(action));
}
}
OfInt
/**
* A Spliterator specialized for {@code int} values.
* @since 1.8
*/
public interface OfInt extends OfPrimitive<Integer, IntConsumer, OfInt> {
@Override
OfInt trySplit();
@Override
boolean tryAdvance(IntConsumer action);
@Override
default void forEachRemaining(IntConsumer action) {
do { } while (tryAdvance(action));
}
/**
* {@inheritDoc}
* @implSpec
* If the action is an instance of {@code IntConsumer} then it is cast
* to {@code IntConsumer} and passed to
* {@link #tryAdvance(java.util.function.IntConsumer)}; otherwise
* the action is adapted to an instance of {@code IntConsumer}, by
* boxing the argument of {@code IntConsumer}, and then passed to
* {@link #tryAdvance(java.util.function.IntConsumer)}.
*/
@Override
default boolean tryAdvance(Consumer<? super Integer> action) {
if (action instanceof IntConsumer) {
return tryAdvance((IntConsumer) action);
}
else {
if (Tripwire.ENABLED)
Tripwire.trip(getClass(),
"{0} calling Spliterator.OfInt.tryAdvance((IntConsumer) action::accept)");
return tryAdvance((IntConsumer) action::accept);
}
}
/**
* {@inheritDoc}
* @implSpec
* If the action is an instance of {@code IntConsumer} then it is cast
* to {@code IntConsumer} and passed to
* {@link #forEachRemaining(java.util.function.IntConsumer)}; otherwise
* the action is adapted to an instance of {@code IntConsumer}, by
* boxing the argument of {@code IntConsumer}, and then passed to
* {@link #forEachRemaining(java.util.function.IntConsumer)}.
*/
@Override
default void forEachRemaining(Consumer<? super Integer> action) {
if (action instanceof IntConsumer) {
forEachRemaining((IntConsumer) action);
}
else {
if (Tripwire.ENABLED)
Tripwire.trip(getClass(),
"{0} calling Spliterator.OfInt.forEachRemaining((IntConsumer) action::accept)");
forEachRemaining((IntConsumer) action::accept);
}
}
}
IntConsumer 和 Consumer 是沒有任何的關聯關系 的。但是為什么能(IntConsumer)Consumer
因為jdk自帶裝箱拆箱操作。 int 和 integer 重疊了。
Java8(7)流源結構代碼分析
流調用機制與原理大揭秘。
流執行操作時,先整理把中間的操作整合,當調用終止操作的時候,對每個元素單個的進行所有的操作。操作中還帶有短路操作。
記錄下來,然后給別人再講,你就掌握了。
學完之后忘記了怎么辦?記錄下來。 筆記 博客。 死記硬背是沒有任何用的。
ReferencePipeline
/**
* Abstract base class for an intermediate pipeline stage or pipeline source
* stage implementing whose elements are of type {@code U}.
*/
//引用管道
//ReferencePipeline 表示流的源階段與中間階段。
//ReferencePipeline.head表示流中的源階段。
abstract class ReferencePipeline<P_IN, P_OUT>
extends AbstractPipeline<P_IN, P_OUT, Stream<P_OUT>>
implements Stream<P_OUT> {
}
AbstractPipeline
/**
* Abstract base class for "pipeline" classes, which are the core
* implementations of the Stream interface and its primitive specializations.
* Manages construction and evaluation of stream pipelines.
*
* <p>An {@code AbstractPipeline} represents an initial portion of a stream
* pipeline, encapsulating a stream source and zero or more intermediate
* operations. The individual {@code AbstractPipeline} objects are often
* referred to as <em>stages</em>, where each stage describes either the stream
* source or an intermediate operation.
流管道的初始的一部分。
*
* <p>A concrete intermediate stage is generally built from an
* {@code AbstractPipeline}, a shape-specific pipeline class which extends it
* (e.g., {@code IntPipeline}) which is also abstract, and an operation-specific
* concrete class which extends that. {@code AbstractPipeline} contains most of
* the mechanics of evaluating the pipeline, and implements methods that will be
* used by the operation; the shape-specific classes add helper methods for
* dealing with collection of results into the appropriate shape-specific
* containers.
*避免自動拆箱和裝箱操作。
* <p>After chaining a new intermediate operation, or executing a terminal
* operation, the stream is considered to be consumed, and no more intermediate
* or terminal operations are permitted on this stream instance.
* 當鏈接完一個新的中間操作或者執行了終止操作之后, 這個流被認為被消費了。不允許再被操作了。
* @implNote
* <p>For sequential streams, and parallel streams without
* <a href="package-summary.html#StreamOps">stateful intermediate
* operations</a>, parallel streams, pipeline evaluation is done in a single
* pass that "jams" all the operations together. For parallel streams with
* stateful operations, execution is divided into segments, where each
* stateful operations marks the end of a segment, and each segment is
* evaluated separately and the result used as the input to the next
* segment. In all cases, the source data is not consumed until a terminal
* operation begins.
只有終止操作開始的時候,源數據才會被消費。
* @param <E_IN> type of input elements
* @param <E_OUT> type of output elements
* @param <S> type of the subclass implementing {@code BaseStream}
* @since 1.8
*/
abstract class AbstractPipeline<E_IN, E_OUT, S extends BaseStream<E_OUT, S>>
extends PipelineHelper<E_OUT> implements BaseStream<E_OUT, S> {
}
內部類,和lambda表達式之間的關系。
本質上 內部類和lambda不是一回事。只是能完成相同的操作。
lambda不是匿名內部類的語法糖,或者說是縮寫。是一種新的形式。
public class LambdaTest {
//內部類,和lambda表達式之間的關系。
Runnable r1 = () -> System.out.println(this); // this表示當前類的對象
//匿名內部類
Runnable r2 = new Runnable() { //
@Override
public void run() {
System.out.println(this);
// this 表示匿名內部類的對象
}
};
public static void main(String[] args) {
LambdaTest lambdaTest = new LambdaTest();
Thread t1 = new Thread(lambdaTest.r1);
t1.start();
System.out.println("- - -- - ");
Thread t2 = new Thread(lambdaTest.r2);
t2.start();
//輸出結果。
//com.sinosoft.lis.test.LambdaTest@62661526
//com.sinosoft.lis.test.LambdaTest$1@59a30351
}
}
使用了模板方法模式。
流是惰性的,是延遲操作的。遇到終止操作時,才會執行操作。
TerminalOp。 終止操作的接口類。
終止操作 只有四種類型, findOp foreachOp matchOp reduceOp
PipelineHelper
stream中間操作與終止操作層次體系分析與設計思想分析
中間操作
BaseStream -》 AbStractpipeline -》ReferencePipeline -》 Head || StatelessOP || statefulOp
最頂層的源 很多源的成員變量 管道 構造流源 無狀態的中間操作 有狀態的中間操作
流是惰性的,是延遲操作的。遇到終止操作時,才會執行操作。再沒有終止操作之前,在整合中間操作(Sink)。
終止操作
TerminalOp -》 FindOp || ForeachOp || MatchOp || reduceOp
最頂層的
TerminalSink
終止的飲水槽。
Java8(8)時間日期API
joda-time
在開始學習jdk8.time之前,先接觸一下joda-time。
public static void main(String[] args) {
// 基本使用方式.
DateTime today = new DateTime();
DateTime dateTime = today.plusDays(1);
//今天
System.out.println(today.toString("yyyy-MM-dd"));
//明天
System.out.println(dateTime.toString("yyyy-MM-dd"));
System.out.println("- - - - -");
//當月的第一天
DateTime dateTime1 = today.withDayOfMonth(1);
System.out.println(dateTime1.toString("yyyy-MM-dd"));
// 當前時間后邊三個月的第后一天的日期
LocalDate localDate = new LocalDate();
localDate = localDate.plusMonths(3).dayOfMonth().withMaximumValue();
System.out.println(localDate);
// 當前時間后邊三個月的第一天的日期
localDate = localDate.plusMonths(3).dayOfMonth().withMinimumValue();
System.out.println(localDate);
//計算兩年前的第三個月的最后一天的時期
DateTime localDate1 = new DateTime();
localDate1.minusYears(2).monthOfYear().setCopy(3).dayOfMonth().withMaximumValue();
System.out.println(localDate1);
}
- example:
public class JodaTest2 {
// 標准UTC時間. 轉換成日期類型 2014-11-11T02:22:22.222z
public static Date to2c(String date) {
//服務器端轉換成客戶端的時間
DateTime parse = DateTime.parse(date, DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ"));
return parse.toDate();
}
public static String toString(Date date) {
// 客戶端的時間轉成服務器的時間
DateTime date1 = new DateTime(date, DateTimeZone.UTC);
return date1.toString();
}
public static String date2String(Date date,String dateFort) {
DateTime dateTime = new DateTime(date);
return dateTime.toString(dateFort);
}
public static void main(String[] args) {
System.out.println(JodaTest2.to2c("2014-11-11T02:22:22.222z"));
System.out.println(JodaTest2.toString(new Date())); // 標准的時間為 差8個小時
System.out.println(JodaTest2.date2String(new Date(),"yyyy-MM-dd"));
}
}
Java中日期時間的api
Java8中的所有時間都是不可變的,確保了線程安全。
沒有必要去研究源代碼。會用就可以了。省下時間去學習更重要,更有價值的事情上。
{
public static void main(String[] args) {
LocalDate localDate = LocalDate.now();
System.out.println(localDate);
//獲取年
System.out.println(localDate.getYear());
//獲取月
System.out.println(localDate.getMonthValue());
//根據年月日構造
LocalDate localDate1 = LocalDate.of(2030, 3, 22);
System.out.println(localDate1);
//根據是時分秒構造
LocalDate localDate2 = LocalDate.of(2020,3,25);
MonthDay monthDay = MonthDay.of(localDate2.getMonth(), localDate2.getDayOfMonth());
//根據是時分秒構造
LocalTime localTime = LocalTime.now();
System.out.println(localTime);
// + 20分鍾, -2個小時
LocalTime localTime1 = localTime.plusMinutes(20).minusHours(2);
System.out.println(localTime1);
System.out.println("- - - - -");
//現在的時間增加兩周 (增加的長度,增加的單位)
LocalDate localDate3 = LocalDate.now().plus(2, ChronoUnit.WEEKS);
System.out.println(localDate3);
//現在的時間減兩周
LocalDate localDate4 = localDate.minus(2, ChronoUnit.MONTHS);
System.out.println(localDate4);
// Clock對象
Clock clock = Clock.systemDefaultZone();
System.out.println(clock);
// 兩個日期進行的判斷
LocalDate localDate5 = LocalDate.now();
LocalDate localDate6 = LocalDate.of(2020,1,21);
System.out.println(localDate5.isBefore(localDate6));
System.out.println(localDate5.isAfter(localDate6));
System.out.println(localDate5.equals(localDate6));
//關於時區的概念.
Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
availableZoneIds.forEach(System.out::println);
//將上邊的無序的時區set進行排序
Set treeSet = new TreeSet<String>(){
{addAll(availableZoneIds);}
};
treeSet.stream().forEach(System.out::println);
//使用時區做一些例子.
ZoneId zoneId = ZoneId.of("Asia/Shanghai");
LocalDateTime localDateTime = LocalDateTime.now();
System.out.println(localDateTime);
ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, zoneId);
System.out.println(zonedDateTime);
System.out.println("- - -- - -");
// 年月的對象
YearMonth yearMonth = YearMonth.now();
System.out.println(yearMonth);
System.out.println(yearMonth.lengthOfMonth());
System.out.println(yearMonth.isLeapYear());
YearMonth yearMonth1 = YearMonth.of(2019, 2);
System.out.println(yearMonth1);
System.out.println(yearMonth1.lengthOfMonth());
System.out.println(yearMonth1.lengthOfYear());
System.out.println(yearMonth1.isLeapYear()); // 是否閏年
LocalDate localDate7 = LocalDate.now();
LocalDate localDate8 = LocalDate.of(2017, 3, 22);
// Period 周期性的.. 比較兩個年份的差別
Period period = Period.between(localDate7, localDate8); //
System.out.println(period);
System.out.println(period.getDays());
System.out.println("- - -- - - ");
// Instant 獲取不帶時區的UTC的標准時間.
System.out.println(Instant.now());
//,,, 剩下的用到的使用自行Google
}
}
Java8(回顧總結)
Java8的回顧和復盤
總共50節課,從開始到結束。學習到的不止是技術,更多的是學習方法。
系統的學習jdk8
- Java 8新特性介紹
- Lambda表達式介紹
- 使用Lambda表達式代替匿名內部類
- Lambda表達式的作用
- 外部迭代與內部迭代
- Java Lambda表達式語法詳解
- 函數式接口詳解
- 傳遞值與傳遞行為
- Stream深度解析
- Stream API詳解
- 串行流與並行流
- Stream構成
- Stream源生成方式
- Stream操作類型
- Stream轉換
- Optional詳解
- 默認方法詳解
- 方法與構造方法引用
- Predicate接口詳解
- Function接口詳解
- Consumer接口剖析
- Filter介紹
- Map-Reduce講解、中間操作與終止操作
- 新的Date API分析
更多的時間是了解底層是怎么實現的。
基礎的重要性
2020年02月07日12:03:41 將Java8學習的筆記給整理到了一個文件當中,方便整理。