Java8 學習筆記
文章目錄
1.Lambda表示式
可以把Lambda表示式理解為簡潔地表示可傳遞的匿名函式的一種方式:它沒有名稱,但它有引數列表、函式主體、返回型別,可能還有一個可以丟擲的異常列表,如下所示簡單的Lamdda表示式:
2.函式式介面
函式式介面定義且只定義了一個抽象方法,需要使用@FunctionalInterface,個標註用於表示該介面會設計成一個函式式介面 函式式介面的抽象方法的簽名基本上就是Lambda表示式的簽名。我們將這種抽象方法叫作函式描述符
常用函式式介面
函式式介面 | 函式描述符 | 原始型別特化 |
---|---|---|
Predicate | T->boolean | IntPredicate,LongPredicate, DoublePredicate |
Consumer | T->void | IntConsumer,LongConsumer, DoubleConsumer |
Function<T,R> | T->R | IntFunction,IntToDoubleFunction,IntToLongFunction, LongFunction,LongToDoubleFunction, LongToIntFunction,DoubleFunction, ToIntFunction,ToDoubleFunction,ToLongFunction |
Supplier | ()->T | BooleanSupplier,IntSupplier, LongSupplier,DoubleSupplier |
UnaryOperator | T->T | IntUnaryOperator,LongUnaryOperator, DoubleUnaryOperator |
BinaryOperator | (T,T)->T | IntBinaryOperator,LongBinaryOperator, DoubleBinaryOperator |
BiPredicate<L,R> | (L,R)->boolean | |
BiConsumer<T,U> | (T,U)->void | ObjIntConsumer,ObjLongConsumer, ObjDoubleConsumer |
BiFunction<T,U,R> | (T,U)->R | ToIntBiFunction<T,U>,ToLongBiFunction<T,U>, ToDoubleBiFunction<T,U> |
3.方法引用
先前:
inventory.sort((Apple a1, Apple a2)-> a1.getWeight().compareTo(a2.getWeight()));
使用方法引用和 java.util.Comparator.comparing:
inventory.sort(comparing(Apple::getWeight)); //使用“::”這個來呼叫方法,
一般沒有對原始資料做其他特殊處理,可以用方法引用的更直觀簡單,如果沒辦法還是得用Lambda表示式。
4.使用流
4.1.流主要是對集合的操作處理
filter:謂詞篩選
foreach:輸出每一個元素
distinct:篩選掉相同元素
limit(n):截斷流,獲取指定的前n個數據
skip(n):跳過集合前n個元素
map:表示對映元素,集合中的一個元素對映到一個新的元素
flatMap:扁平化集合,可以把兩個集合合併成一個集合
allMatch:流中是否有一個元素能匹配給定的謂詞
anyMatch:流中的元素是否都能匹配給定的謂詞
noneMatch:它可以確保流中沒有任何元素與給定的謂詞匹配
findFirst:將返回當前流中的任意元素
findAny:找到當前流中的第一個元素
reduce:規約資料(對每一個元素做累積值,可以求和,積,求最大值,最小值,可以給定初始值,也可以不給定初始值)
count:計算流中元素的個數
sorted:排序
如下具體的詳情描述:
flatMap例子一:
String[] words = {"Goodbye", "World"}; //如下通過flatMap可以獲取這個陣列中包含的所有字元(不重複)
List<String> uniqueCharacters =words.stream()
.map(w -> w.split(""))
.flatMap(Arrays::stream)
.distinct()
.collect(Collectors.toList());
flatMap例子二:
//如下可以讓兩個陣列的數自由的組合
List<Integer> numbers1 = Arrays.asList(1, 2, 3);
List<Integer> numbers2 = Arrays.asList(3, 4);
List<int[]> pairs =numbers1.stream()
.flatMap(i -> numbers2.stream().map(j -> new int[]{i, j}))
.collect(toList());
reduce帶初始值例子三:
int sum = numbers.stream().reduce(0, Integer::sum);
reduce不帶初始值例子三:
Optional<Integer> sum = numbers.stream().reduce((a, b) -> (a + b));
4.2.流轉換
1)對映到基本型別流
mapToInt:對映基本型別int的流, IntStream 還支援其他的方便方法,如max 、 min 、 average 等
mapToDouble:對映基本型別double的流
mapToLong:對映基本型別long的流
2)轉換回為物件流
boxed:相當於對基本型別的裝箱
mapToObject:這個也可以實現
3)數值範圍
range,rangeClosed
這兩個方法都是第一個引數接受起始值,第二個引數接受結束值。但
range 是不包含結束值的,而 rangeClosed 則包含結束值
例如:
IntStream.rangeClosed(1, 100) //這個範圍是包含100的,結果的資料有50個
.filter(n -> n % 2 == 0)
IntStream.range(1, 100)
.filter(n -> n % 2 == 0) //這個範圍是不包含100的,結果的資料只有49個
4.3 建立流
1)使用Stream.of建立
Stream<String> stream = Stream.of("Java 8 ", "Lambdas ", "In ", "Action");
2)由陣列建立流
int[] numbers = {2, 3, 5, 7, 11, 13};
int sum = Arrays.stream(numbers).sum();
3)檔案流
如下通過Files.lines建立流
long uniqueWords = 0;
try(Stream<String> lines =Files.lines(Paths.get("data.txt"), Charset.defaultCharset())){
uniqueWords = lines.flatMap(line -> Arrays.stream(line.split(" ")))
.distinct()
.count();
}
catch(IOException e){
}
4)建立無限流
Stream API提供了兩個靜態方法來從函式生成流: Stream.iterate 和Stream.generate 。 這兩個操作可以建立所謂的無限流:不像從固定集合建立的流那樣有固定大小的流。由 iterate
和 generate 產生的流會用給定的函式按需建立值,因此可以無窮無盡地計算下去!一般來說,應該使用 limit(n) 來對這種流加以限制,以避免列印無窮多個值。
i) iterate 方法要接受一個 UnaryOperator 作為引數,表示不停的迭代上一個數
例子一:
//Stream.iterate 如下是生成2,4,6,8...總共10個偶數,都是前一個數加2所得
Stream.iterate(0, n -> n + 2)
.limit(10)
.forEach(System.out::println);
例子二:
//Stream.iterate 可以用來生成斐波那契函式
Stream.iterate(new int[]{0, 1},t -> new int[]{t[1],t[0] + t[1]})
.limit(10)
.map(t -> t[0])
.forEach(System.out::println);
//這段程式碼將生成斐波納契數列:0, 1, 1, 2, 3, 5, 8, 13, 21, 34…
ii) generate 方法也可讓你按需生成一個無限流。但 generate 不是依次對每個新生成的值應用函式的。它接受一個 Supplier 型別的Lambda提供新的值。
例子一:
Stream.generate(Math::random)
.limit(5)
.forEach(System.out::println);//生成新的5個函式
例子二:
//同樣生成斐波那契函式,不過這個存在有中間狀態,在並行條件下是不安全的,不建議這樣用
IntSupplier fib = new IntSupplier(){
private int previous = 0;
private int current = 1;
public int getAsInt(){
int oldPrevious = this.previous;
int nextValue = this.previous + this.current;
this.previous = this.current;
this.current = nextValue;
return oldPrevious;
}
};
IntStream.generate(fib).limit(10).forEach(System.out::println);
5.收集流
收集流可以對流使用collect方法,然後使用Collector類提供的方法進行收集。
它們主要提供了三大功能:
a. 將流元素歸約和彙總為一個值
b. 元素分組
c. 元素分割槽
方法 | 說明 |
---|---|
Collector.toList() | 將流轉換為一個List集合 |
Collector.toSet | 將流轉換為一個Set集合,不包含重複元素 |
Collector.toSet | 將流收集起來成為集合 |
Collectors.counting() | 可以返回流中有多少個元素 |
Collectors.maxBy(Comparator) | 可以返回流裡最大的元素(某個條件) |
Collectors.minBy(Comparator) | 可以返回流裡最小的元素(某個條件) |
Collectors.summingInt(Function) | 對於流中的元素求和,返回int,同樣summingDouble則是返回double型別 |
Collectors.averagingDouble(Function) | 對流中的元素求平均值返回double型別,同樣有averagingInt,averagingLong |
Collectors.joining() | 對CharSequence的元素流進行組合,可以使用帶引數做分割,以及前後新增元素 |
Collectors.reducing (U identity,Function<? super T, ? extends U> mapper,BinaryOperator op) | 上面所有的都是這個reducing的特殊情況,都可以通過這個方法實現,這個有三個過載方法。 |
Collectors.groupingBy(Function) | 對於流通過某一個元素進行分組,這個也支援巢狀分組,返回Map集合,第二個引數可以對生成的集合進行求和,數量計算,最大,最小等 |
Collectors.collectingAndThen | 把結果集轉換為另外一個結果,一般可以對包裝的Optional進行get提取元素 |
Collectors.partitioningBy(Predicate) | 對流進行分割槽,和GroupBy相似,不過只分為true和false兩組;他和可以和groupingBy巢狀使用 |
6.流中插入動作peek
peek 的設計初衷就是在流的每個元素恢復執行之前,插入執行一個動作。但是它不像 forEach 那樣恢復整個流的執行,而是在一個元素上完成操作之後,它只會將操作順承到流水線中的下一個操作。