java8實戰學習

niewj 發表於 2022-05-17
Java Java 8

[toc]


java8實戰學習

1. lambda表示式

1.1 什麼是lambda表示式

1.2 什麼樣的場景能使用lambda表示式

1.3 lambda表示式實現一個介面的四種寫法

2. 函數語言程式設計

2.1 什麼是函數語言程式設計

2.2 什麼是指令式程式設計

2.3 什麼是函式式介面 FunctionalInterface

2.4 什麼是 default 方法

2.5 default方法的意義

2.6 java8內建的常用函式式介面

(1). Predicate<T>

斷言 -> 輸入T, 輸出 boolean

(2). Consumer<T>

消費一個輸入 -> 輸入T, 不輸出(void)

(3). Supplier<T>

生產一個輸出 -> 不輸入, 輸出T

(4). Function<T, R>

輸入T, 輸出R的函式

(5). UnaryOperator<T>

一元函式: 輸入1個輸出 1個: 型別都是T

(6). BinaryOperatior<T>

二元函式: 輸入2個輸出1個: 型別都是T

(7). BiFunction<T, U, R>

輸入兩個輸出一個: 輸入 T, U 輸出 R, 常用於 reduce/sort等操作,

2.7 方法引用

(1). 靜態方法的方法引用
(2). 例項方法的方法引用
(3). 構造方法的方法引用

2.8. 變數引用和隱式final

為何內部類使用外部變數要是final的

3. stream流式程式設計

3.1. 外部迭代和內部迭代

(1). 什麼是外部迭代
(2). 什麼是內部迭代

3.2. 中間操作/終止操作/惰性求值

(1). 什麼是中間操作?

返回還是流stream的操作, 就是中間操作, 例如 map操作

map
flatMap
distinct
limit
skip
filter
peek
sorted
(1.1) 有狀態操作

有前後順序依賴關係的操作, 比如
distinct(前後不能相同)
sorted(前後順序要求)
limit/skip(擷取前面的/跳過前面的)

(1.2) 無狀態操作

無前後依賴關係的操作:
map/mapToInt/flatMap/flatMapToInt
filter
peek

(2). 什麼是終止操作?

返回一個結果的(求和/彙總)等, 例如 sum/max/min/avg等

forEach
collect
reduce
toArray
min/max/count
findAny
findFirst
allMatch
anyMatch
noneMatch
(1.1) 短路操作

findFirst
findAny
allMatch
anyMatch
noneMatch

(1.2) 非短路操作

forEach
forEachOrdered 並行流中保證順序的
collect
toArray
reduce 輸入: BinaryOperator<T>
min
max
count

(3). 什麼是惰性求值?

如果沒有終止操作, 中間的操作動作都不會實際執行, 這就是惰性求值;

3.3 stream流的建立

(1) 集合

collection.stream
collection.parallelStream

(2) 陣列

array.stream
Stream.of

(3). 數字
IntStream

IntStream.range
IntStream.rangeClosed

LongStream

LongStream.range
LongStream.rangeClosed

Random

Random.longs
Random.ints
Random.doubles

(4).自己建立

Stream.generate
Stream.iterate

3.4 並行流

(1). parallel()
(2). 並行流預設使用的執行緒池

ForkJoinPool.commonPool

(3). 自定義執行緒池

如何自定義執行緒池, 並使用到流並行時?

3.5 收集器 collect

(1). 收集物件的屬性列表為一個新集合

collect(Collectors.toList())

(2). 統計資訊彙總

IntSummaryStatistics ageSumStats = students.stream().collect(Collectors.summarizingInt(Student::getAge))

(3). 分塊:使用斷言(一分為二)

Map<Boolean, List<Student>> genders = students.stream().collect(Collectors.partitioningBy(s->s.getGender() == Gender.MALE))

(4). 分組:使用屬性(一分為N, 類似sql groupby)

Map<Grade, List<Student>> grades = students.stream().collect(Collectors.groupingBy(Student::getGrade))

(5). 分組並彙總計數

Map<Grade, Long> gradeCount = students.stream().collect(Collectors.groupingBy(Student::getGrade, Collectors.counting()));
counting()可以替換為同類的max/min/avg等

(6). 小結
Collectors.toList()
Collectors.toCollection()
Collectors.summarizingInt(x)
Collectors.partitioningBy(x)
Collectors.groupingBy(x)
Collectors.counting()

3.6 stream執行機制

(1). 鏈式呼叫

所有操作鏈式呼叫, 1個元素只迭代一次;

(2). 屬性sourceStage

每一箇中間操作返回一個新的stream流, 流內有一個屬性: sourceStage; 指向同一個地方: 即 連結串列的頭 Head

(3). Head -> nextStage -> nextStage -> null

stream介面的實現類
java.util.stream.AbstractPipeline
java.util.stream.ReferencePipeline
屬性: sourceStage --> ReferencePipeline$Head
Stream流繼承關係

(4). 無狀態操作執行機制

完全鏈式呼叫: s1.a操作完->s1.b

(5). 有狀態操作執行機制
public void testStream() {
    Random r = new Random();
    Stream.generate(() -> r.nextInt()).limit(500)
            .peek(s -> System.out.println("peek: " + s)) // 1
            .filter(s -> {  // 2
                System.out.println("filter: " + s);
                return s > 1000000;
            })
            .sorted((i1, i2) -> { // 3.
                System.out.println("排序:" + i1 + ", " + i2);
                return i1.compareTo(i2);
            })
            .peek(s -> System.out.println("peek2:" + s)) //4.
            .count(); 
}

1,2 都是無狀態操作; 3是有狀態操作; 4是無狀態操作
3的有狀態操作會截斷 1, 2的無狀態操作;
本來是1, 2的鏈式呼叫: s1.a->s1.b->s1.c 現在會變成: s1.a() ->s2.a() -> s1.b() ->s2.b()
output: 無sorted: 可以看到純無狀態操作是純鏈式: peek->filter->peek2

peek: 2047843427
filter: 2047843427
peek2:2047843427
peek: -1210662664
filter: -1210662664
peek: 835825054
filter: 835825054
peek2:835825054
peek: 2068471207
filter: 2068471207
peek2:2068471207
peek: -1139851578
filter: -1139851578
peek: -885776051
filter: -885776051
peek: 481902862
filter: 481902862
peek2:481902862
peek: 684461691
filter: 684461691
peek2:684461691
peek: 1417449012
filter: 1417449012
peek2:1417449012
peek: -40633821
filter: -40633821

output: 有sorted: 可以看到後面的無狀態操作peek2被有狀態的sorted打斷, 變成: peek->filter... peek->filter 排序all, peek2 all

peek: -607778068
filter: -607778068
peek: 50926402
filter: 50926402
peek: -774310924
filter: -774310924
peek: 342023904
filter: 342023904
peek: 26606322
filter: 26606322
peek: 693727663
filter: 693727663
peek: -334751306
filter: -334751306
peek: -960784614
filter: -960784614
peek: 522967780
filter: 522967780
peek: -2144851449
filter: -2144851449
排序:342023904, 50926402
排序:26606322, 342023904
排序:26606322, 342023904
排序:26606322, 50926402
排序:693727663, 50926402
排序:693727663, 342023904
排序:522967780, 342023904
排序:522967780, 693727663
peek2:26606322
peek2:50926402
peek2:342023904
peek2:522967780
peek2:693727663

1, 2還是鏈式呼叫; 中間的3執行完後, 隔斷了4, 所以4不會和1,2承續鏈式呼叫, 而是單獨執行;

(6) 並行操作: parallel()

在4後加5: parallel():
sorted() 有狀態操作不會並行; 1,2,4都會使用 ForkJoinPool執行緒池並行執行:

@Test
public void testStream() {
    Random r = new Random();
    long count = Stream.generate(() -> r.nextInt()).limit(10)
            .peek(s -> System.out.println(Thread.currentThread().getName() + " peek: " + s)) // 1
            .filter(s -> {  // 2
                System.out.println(Thread.currentThread().getName() + " filter: " + s);
                return s > 1000000;
            })
            .sorted((i1, i2) -> { // 3.
                System.out.println(Thread.currentThread().getName() + " 排序:" + i1 + ", " + i2);
                return i1.compareTo(i2);
            })
            .peek(s -> System.out.println(Thread.currentThread().getName() + " peek2:" + s)) //4.
            .parallel()
            .count();
    System.out.println(count);
}
ForkJoinPool.commonPool-worker-1 peek: -1644694686
ForkJoinPool.commonPool-worker-1 filter: -1644694686
ForkJoinPool.commonPool-worker-1 peek: 1524371421
ForkJoinPool.commonPool-worker-1 filter: 1524371421
ForkJoinPool.commonPool-worker-1 peek: -1937453784
ForkJoinPool.commonPool-worker-1 filter: -1937453784
ForkJoinPool.commonPool-worker-1 peek: 991114309
ForkJoinPool.commonPool-worker-1 filter: 991114309
ForkJoinPool.commonPool-worker-1 peek: -109655961
ForkJoinPool.commonPool-worker-1 filter: -109655961
ForkJoinPool.commonPool-worker-1 peek: 878490064
ForkJoinPool.commonPool-worker-1 filter: 878490064
ForkJoinPool.commonPool-worker-1 peek: 2031919515
ForkJoinPool.commonPool-worker-1 filter: 2031919515
ForkJoinPool.commonPool-worker-1 peek: -1855129379
ForkJoinPool.commonPool-worker-1 filter: -1855129379
ForkJoinPool.commonPool-worker-1 peek: 1897985020
ForkJoinPool.commonPool-worker-1 filter: 1897985020
ForkJoinPool.commonPool-worker-1 peek: 352116584
ForkJoinPool.commonPool-worker-1 filter: 352116584
main 排序:991114309, 1524371421
main 排序:878490064, 991114309
main 排序:2031919515, 878490064
main 排序:2031919515, 991114309
main 排序:2031919515, 1524371421
main 排序:1897985020, 1524371421
main 排序:1897985020, 2031919515
main 排序:352116584, 1524371421
main 排序:352116584, 991114309
main 排序:352116584, 878490064
ForkJoinPool.commonPool-worker-6 peek2:991114309
main peek2:1524371421
ForkJoinPool.commonPool-worker-3 peek2:352116584
ForkJoinPool.commonPool-worker-2 peek2:2031919515
ForkJoinPool.commonPool-worker-7 peek2:1897985020
ForkJoinPool.commonPool-worker-4 peek2:878490064
6

結論: 有狀態的並行操作"不一定"並行 為啥是"不一定"?

因為官方並未明說, 但是觀察下來, 是非並行的!

3.7 小結鏈式呼叫原理:

  1. stream鏈式呼叫原理: ReferencePipeline
  2. 每個中間操作產生一個流(新的流)
  3. 新流和原流的關係: 新流內有個sourceStage->指向原流的Head節點(ReferencePipeline$Head)
  4. 鏈式呼叫的維護: ReferencePipeline中的 nextStage -> nextStage -> null來維護鏈;

3.8 小結2

  1. 有狀態操作 破壞鏈式呼叫
  2. 並行操作中有狀態操作不一定並行, 如排序!