流是一系列與特定存儲機制無關的元素——實際上,流並無「存儲」之說。php
使用流,無需迭代集合中的元素,就能夠提取/操做特定的元素java
假設我要生成一個隨機序列,範圍在5到100之間,不重複,隨機生成7個數字,並且要排序,最後輸出序列,能夠這麼作:python
public static void main(String[] args) { new Random(47) // 種子 .ints(5, 100) .distinct() .limit(7) .sorted() .forEach(System.out::println); }
整個過程是一個工做流,它不須要單獨提取出這個序列在進行操做。c++
若是要實現剛剛的功能,而不使用流,就可能會是這樣子:正則表達式
public static void main(String[] args) { Random random = new Random(47); SortedSet<Integer> set = new TreeSet<>(); while (set.size() < 7){ int r = random.nextInt(100); if(r >= 5){ set.add(r); } } System.out.println(set); }
流操做的類型有三種:數據庫
.forEach()
.sum()
能夠用Steam.of
將對象數組/一組元素轉換爲 流編程
String[] arr = {"C++ ", "Python ", "Java"}; Stream.of(arr).forEach(System.out::print); Stream.of("C++ ", "Python ", "Java ").forEach(System.out::print);
每一個集合也能夠用.stream()
來產生一個流數組
List<String> strList = Arrays.asList("C++ ", "Python ", "Java "); strList.stream().forEach(System.out::print); // C++ Python Java
對於原始數據類型的數組/列表,能夠用Arrays.stream()
安全
Steam.of()
,輸出的是一個地址int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; Arrays.stream(arr).forEach(System.out::println); // 輸出元素1 2 3 4 ... Stream.of(arr).forEach(System.out::println); // 輸出地址 [I@214c265e
生成4個隨機整數,輸出服務器
ints(n)
:控制流的大小爲4,生成4個整數,返回一個IntStream
forEach(System.out::println)
:打印每一個元素Random random = new Random(47); // 47是參數,種子 random.ints(4).forEach(System.out::println);
生成5個隨機整數,只取前3個整數
Random random = new Random(); random.ints(5).limit(3).forEach(System.out::println);
建立6個隨機整數,範圍在10~20之間
ints(m,n)
:生成[m,n)
之間的整數,返回一個IntStream
ints(m,n)
的方法,要加limit()
,否則就是無限循環Random random = new Random(); random.ints(10,20).limit(6).forEach(System.out::println);
也能夠用init(streamSize,numberOrigin,numberBound)
來建立:
Random random = new Random(); random.ints(6,10,20).forEach(System.out::println);
假設我要建立一個[1,100]
的連續序列,求它的和,能夠用 IntStream
提供的 range(m,n)
方法
public static void main(String[] args) { // 1-100的序列數組 int[] intsArr = IntStream.range(1, 101).toArray(); int sum = Arrays.stream(intsArr).sum(); System.out.println(sum); }
能夠寫的再簡單一點:
public static void main(String[] args) { // 1-100的序列數組 int sum = IntStream.range(1, 101).sum(); System.out.println(sum); }
對比傳統的方法,就會以爲用 流 會方便不少:
// 傳統方法 public static void main(String[] args) { int sum = 0; for(int i = 1; i <= 100;++i){ sum += i; } System.out.println(sum); }
Stream.generate()
建立Stream.generate()
返回無限流,咱們能夠自定義生成的元素
生成5個隨機整數
Stream.generate(() -> { return new Random().nextInt(); }).limit(5).forEach(System.out::println);
也能夠簡寫成:
Stream.generate(new Random()::nextInt).limit(5).forEach(System.out::println);
Stream.iterate()
建立方法原型:
public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) { // ... }
第一個參數:種子
第二個參數:方法(lambda表達式)
將 種子 傳遞給 方法,方法的運行結果 做爲流的下一個元素,添加到流中,存儲起來,做爲下次調用iterate()
的第二個參數
用iterate()
生成斐波那契數列
iterate()
只記憶結果
public class Generator { private int x = 0; Stream<Integer> numbers(){ return Stream.iterate(1,(i)->{ int res = x + i; x = i; return res; }); } public static void main(String[] args) { // 生成前10項 new Generator().numbers().limit(10).forEach(System.out::println); // 生成第10到20項 new Generator() .numbers() .skip(10) // 過濾前10個 .limit(10) // 而後取10個 .forEach(System.out::println); } }
Arrays.stream()
和 Stream.of()
Arrays
類中含有一個名爲 stream()
的靜態方法用於把 int/long/double
數組 轉換成爲流。
int、double、long等基本類型的數組,用 Arrays.stream(數組名)
進行流的操做
若是是String(String是一個對象),能夠用 Stream.of(對象數組名)
進行流的操做
Stream.of()
和 Arrays.stream()
是有區別的:
Integer
、Long
),兩個方法是同樣的結果Integer[] intArr = {1, 2, 3, 4}; Arrays.stream(intArr).forEach(System.out::print); // 1234 Stream.of(intArr).forEach(System.out::print); // 1234
int
、long
),兩個方法返回的結果不同int[] intArr = {1, 2, 3, 4}; Arrays.stream(intArr).forEach(System.out::print); // 1234 Stream.of(intArr).forEach(System.out::print); // [I@214c265e
這是由於int[]
傳入Stream.of()
會被看成一個對象,而傳入Arrays.stream()
會被當成一個數組
什麼是流的中間操做?就是修改流中的元素,返回一個修改後的Stream。
一個流建立以後有6個元素,我跳過前3個,只取最後3個,那中間操做就是 「跳過前三個」
peek()
跟蹤和調試peek()
的目的是:無修改的查看流中的元素
peek()
參數可接收lambda表達式
當流中的一個元素經過管道的時候,就會調用一次peek()
例1
public static void main(String[] args) { String[] str = {"java ", "c++ ", "php ", "dart "}; Stream.of(str) .skip(1) .peek(System.out::print) // 輸出當前流的狀況 .map(s -> "第1次變化:" + s) // 映射,修改str中字符串的值 .peek(System.out::print) .forEach(System.out::println); }
例1-輸出結果:
c++ 第1次變化:c++ 第1次變化:c++ php 第1次變化:php 第1次變化:php dart 第1次變化:dart 第1次變化:dart
例2
public static void main(String[] args) { String[] str = {"java ", "c++ ", "php ", "dart "}; Stream.of(str) .skip(1) .peek(System.out::print) .map(String::toUpperCase) // 映射,將流中的元素全轉成大寫 .forEach(System.out::print); }
例2-輸出結果
c++ C++ php PHP dart DART
sorted()
排序sorted()
接收一個lambda表達式,也能夠接收一個 比較器(sorted()
預設了一些默認的比較器)
基本類型數組排序
我查了資料,發現 基本類型的數組的sorted()
不能傳比較器,它們的包裝類就能夠
public static void main(String[] args) { // 基本類型數組 int[] arr = {1, 18, 12, 16, 4}; // 默認正序 Arrays.stream(arr).sorted().forEach(System.out::println); // 1 4 12 16 18 }
對象/包裝類數組排序
public static void main(String[] args) { // Integer類型數組 Integer[] integerArr = {1, 18, 12, 16, 4}; // 正序 Arrays.stream(integerArr).sorted().forEach(System.out::println); // 1 4 12 16 18 // 逆序 Arrays.stream(arr).sorted(Comparator.reverseOrder()).forEach(System.out::println); // 18 16 12 4 1 }
集合排序
public static void main(String[] args) { // List 集合 List<Integer> list = new LinkedList<>(Arrays.asList(1,18,12,16,4)); // 正序 list.stream().sorted().forEach(System.out::println); // 1 4 12 16 18 // 逆序 list.stream().sorted(Comparator.reverseOrder()).forEach(System.out::println); // 18 16 12 4 1 }
distinct()
去重消除流中重複的元素
public static void main(String[] args) { // 數組 Integer[] arr = {1, 1, 1, 2, 2}; Arrays.stream(arr).distinct().forEach(System.out::println); // 1 2 }
filter(Predicate)
過濾器filter()
接收到參數是一個函數,若是函數返回 true
,則保留這些元素。返回false
則刪除這些元素
假定有一個數組,保留其中的奇數:
public class Main { public static void main(String[] args) { // 數組 Integer[] arr = {1, 2, 3, 4, 5, 6, 7}; Arrays.stream(arr).filter(Main::Odd).forEach(System.out::println); // 1 3 5 7 } // 奇數返回true public static Boolean Odd(int number) { return number % 2 == 1; } }
一樣的,filter()
也接收正則表達式
public static void main(String[] args) { // 數組 Integer[] arr = {1, 2, 3, 4, 5, 6, 7}; Arrays.stream(arr).filter(num -> num % 2 == 1).forEach(System.out::println); }
map()
相關 - 映射到元素map(Function)
:將函數操做應用在輸入流的元素中,並將返回值傳遞到輸出流中。mapToInt(ToIntFunction)
:操做同上,但結果是 IntStream。mapToLong(ToLongFunction)
:操做同上,但結果是 LongStream。mapToDouble(ToDoubleFunction)
:操做同上,但結果是 DoubleStream。public static void main(String[] args) { // 數組 Integer[] arr = {1, 2, 3, 4, 5, 6, 7}; Arrays.stream(arr) .map(s -> s * 3) // 每一個元素 × 3 .forEach(System.out::println); // 2 4 9 16 10 12 14 }
skip(long n)
跳過前n個元素skip(long n)
能夠省略流的前n個元素
public static void main(String[] args) { int[] arr= {1,2,3,4,5}; // 省略前 3 個 Arrays.stream(arr).skip(3).forEach(System.out::println); // 4 5 }
limit(long n)
保留前n個元素limit(long n)
能夠保留前n個元素,其餘的不要
public static void main(String[] args) { int[] arr= {1,2,3,4,5}; // 保留前 3 個 Arrays.stream(arr).limit(3).forEach(System.out::println); // 1 2 3 }
parallel()
流的並行處理parallel()
可實現多處理器並行操做。實現原理爲將流分割爲多個(一般數目爲 CPU 核心數)並在不一樣處理器上分別執行操做。
使用場景(前提是確保線程的安全):
假設這樣一個場景:有一個商品的分類ld,要根據分類Id,去數據庫找具體的信息(IO操做)
傳統的遍歷
public static void main(String[] args) { List<Category> list = getCategoryIdList(); // 獲取商品分類的Id // 普通的遍歷 long start = System.currentTimeMillis(); for (Category category : list) { System.out.println(getById(category.id)); } long end = System.currentTimeMillis(); System.out.println("普通遍歷 運行時間:" + (end - start) + "毫秒"); }
使用 parallel()
遍歷
遍歷的結果是無序的
運行時間:607毫秒
public static void main(String[] args) { List<Category> list = getCategoryIdList(); // 獲取商品分類的Id // 並行遍歷 long start = System.currentTimeMillis(); list.stream().parallel().forEach(category -> { // IO 操做 System.out.println(getById(category.id)); // 根據分類Id,查詢具體信息 }); long end = System.currentTimeMillis(); System.out.println("parallel() 運行時間:" + (end - start) + "毫秒"); }
使用 Java8 的 parallel 能夠加快某些操做的速度,但若是是一些簡單的操做,那就得不償失了
此外,parallel()
遍歷的結果是無序的
終端操做:獲取流的最終結果,沒法再日後傳遞流,好比打印、獲取集合、查找,都算是終端操做
Optional
對象Optional
類 隱藏了可能存在空指針的不肯定性
一些終端操做,會返回一個Optional
對象,由於這些操做,不能保證預期結果必定存在,也就是隱藏了空指針的信息。若是流是空的,那就會返回Option.empty
findFirst()
返回一個包含第一個元素的 Optional 對象,若是流爲空則返回 Optional.empty
findAny()
返回包含任意元素的 Optional 對象,若是流爲空則返回 Optional.empty
max()
和 min()
返回一個包含最大值或者最小值的 Optional 對象,若是流爲空則返回 Optional.empty
reduce()
再也不以 identity
形式開頭,而是將其返回值包裝在 Optional 中。
(identity
對象成爲其餘形式的 reduce()
的默認結果,所以不存在空結果的風險)
findFirst()
public static void main(String[] args) { String [] str1 = {"java","python","c++"}; String [] str2 = {}; System.out.println(Stream.of(str1).findFirst()); // Optional[java] System.out.println(Stream.of(str2).findFirst()); // Optional.empty }
findAny()
public static void main(String[] args) { String [] str1 = {"java","python","c++"}; String [] str2 = {}; System.out.println(Stream.of(str1).findAny()); // Optional[java] System.out.println(Stream.of(str2).findAny()); // Optional.empty }
max()
和 min()
public static void main(String[] args) { Integer[] arr1 = {1, 2, 3, 4, 5}; Integer[] arr2 = {}; System.out.println(Stream.of(arr1).max(Integer::compareTo)); // Optional[5] System.out.println(Stream.of(arr1).min(Integer::compareTo)); // Optional[5] System.out.println(Stream.of(arr2).min(Integer::compareTo)); // Optional.empty System.out.println(Stream.of(arr2).max(Integer::compareTo)); // Optional.empty }
解包 Optional:
isPresent()
:判斷 Optional 對象中是否包含元素ifPresent(Consumer)
:若是 Optional對象中包含元素,就調用 Consumer方法(lambda表達式)get()
:獲取 Optional對象的 元素orElse(otherObject)
:若是值存在則直接返回,不然生成 otherObject。orElseGet(Supplier)
:若是值存在則直接返回,不然使用 Supplier 函數生成一個可替代對象。orElseThrow(Supplier)
:若是值存在直接返回,不然使用 Supplier 函數生成一個異常。toArray()
返回數組toArray()
:將流按照數組返回
toArray(T[] a)
:生成自定義類型的數組
生成隨機整型數組:
public static void main(String[] args) { int[] arr = new Random().ints(5).toArray(); // 生成生成5個int型,轉換爲int數組 }
用toArray(T[] a)
將集合轉成數組
public static void main(String[] args) { List<String> list = new LinkedList<>(); list.add("java "); list.add("python"); String[] str = (String[]) list.toArray(new String[0]); Stream.of(str).forEach(System.out::print); // java python }
forEach()
遍歷forEach(Consumer)
常見如 System.out::println
做爲 Consumer 函數。
forEachOrdered(Consumer)
: 保證 forEach
按照原始流順序操做。(parallel()
操做以後是無序的,用這個能夠強制保持原始流的順序)
兩個函數都接收lambda表達式
使用forEach
遍歷數組:
Integer[] arr = {1, 2, 3, 4, 5, 6, 7}; // 接收lambda表達式 // i 就是數組中的元素 Arrays.stream(arr).forEach(i -> { // do something... });
傳統方法遍歷數組:
Integer[] arr = {1, 2, 3, 4, 5, 6, 7}; for(Integer i : arr){ // do something... }
collect()
返回集合collect(Collector)
:使用 Collector 收集流元素到結果集合中。
collect(Supplier, BiConsumer, BiConsumer)
:同上,第一個參數 Supplier 建立了一個新的結果集合,第二個參數 BiConsumer 將下一個元素收集到結果集合中,第三個參數 BiConsumer 用於將兩個結果集合合併起來。
生成隨機數,保存到 LinkedList中
public static void main(String[] args) { LinkedList<Integer> list = new Random(47) .ints(10) .collect(LinkedList::new, LinkedList::add, LinkedList::addAll); for (Integer integer : list) { System.out.print(integer+ " "); } }
reduce()
組合使用reduce()
組合全部流中的元素
reduce(BinaryOperator)
:使用 BinaryOperator 來組合全部流中的元素。由於流可能爲空,其返回值爲 Optional。
reduce(identity, BinaryOperator)
:功能同上,可是使用 identity 做爲其組合的初始值。所以若是流爲空,identity 就是結果。
reduce(identity, BiFunction, BinaryOperator)
:更復雜的使用形式
identity
:組合函數的標識值,累加器的初始值。
BiFunction
:累加器,一個函數,用於將額外的元素合併到結果中
BinaryOperator
:用於組合兩個值的關聯、不干擾、無狀態函數,必須與累加器函數兼容
只有在並行流中才會執行
使用Stream.reduce()
合併流的元素,併產生單個值
傳統方法使用for循環求和
public static void main(String[] args) { int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; int sum = 0; for(int i : numbers){ sum += i; } System.out.println(sum); // 55 }
使用reduce()
求和
public static void main(String[] args) { int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; int sum = Arrays.stream(numbers).reduce(0, (a, b) -> a + b); System.out.println(sum); }
累加器的初始值設置爲0,(a,b)-> a+b
是累加器,結果保存到第一個參數中
count()
統計統計流中的個數,返回值是long
類型
String[] arr = {"123", "456", "789"}; long cnt = Stream.of(arr).count(); System.out.println(cnt); // 3
max()
和 min()
最大數值max()
和 min()
:數值流操做無需 Comparator。返回一個Optional
對象數組最大值、最小值
public static void main(String[] args) { int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; System.out.println(Arrays.stream(arr).max()); // OptionalInt[10] System.out.println(Arrays.stream(arr).max().getAsInt()); // 10 System.out.println(Arrays.stream(arr).min()); // OptionalInt[1] System.out.println(Arrays.stream(arr).min().getAsInt()); // 1 }
max(Comparator)
和 min(Comparator)
max(Comparator)
:根據所傳入的 Comparator 所決定的「最大」元素。min(Comparator)
:根據所傳入的 Comparator 所決定的「最小」元素。Optional
對象public static void main(String[] args) { String[] str = {"java","c++","python"}; System.out.println(Arrays.stream(str).max(String::compareTo)); // Optional[python] System.out.println(Arrays.stream(str).max(String::compareTo).get()); // python }
sum()
求和求流元素的和
public static void main(String[] args) { int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; int sum = Arrays.stream(arr).sum(); System.out.println(sum); // 55 }
average()
平均值求流元素的平均值,返回一個OptionalDouble
對象
public static void main(String[] args) { int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; OptionalDouble average = Arrays.stream(arr).average(); System.out.println(average); // OptionalDouble[5.5] System.out.println(average.getAsDouble()); // 5.5 }
allMatch(Predicate)
:若是流的每一個元素提供給 Predicate 都返回 true ,結果返回爲 true。在第一個 false 時,則中止執行計算。anyMatch(Predicate)
:若是流的任意一個元素提供給 Predicate 返回 true ,結果返回爲 true。在第一個 true 時中止執行計算。noneMatch(Predicate)
:若是流的每一個元素提供給 Predicate 都返回 false 時,結果返回爲 true。在第一個 true 時中止執行計算。說簡單,就是傳一個lambda表達式,而後返回布爾值
判斷數組中是否全都爲奇數
public static void main(String[] args) { int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; // 判斷全部元素是否都爲奇數 boolean flag = Arrays.stream(arr).allMatch(i -> i % 2 == 1); System.out.println(flag); }
findFirst()
:返回第一個流元素的 Optional,若是流爲空返回 Optional.empty。findAny(
:返回含有任意流元素的 Optional,若是流爲空返回 Optional.empty。public static void main(String[] args) { String[] str = {"java","c++","python"}; System.out.println(Arrays.stream(str).findFirst()); // Optional[java] System.out.println(Arrays.stream(str).findFirst().get()); // Java }