JDK1.8新增了Stream類,從而把函數式編程的風格引入到Java語言中,Stream類的API提供了強大的功能,使用Stream后,可以寫出更加強大,更加簡潔的代碼
首先,Stream流有一些特性:
- Stream流不是一種數據結構,不保存數據,它只是在原數據集上定義了一組操作。
- 這些操作是惰性的,即每當訪問到流中的一個元素,才會在此元素上執行這一系列操作。
- Stream不保存數據,故每個Stream流只能使用一次。
關于應用在Stream流上的操作,可以分成兩種:Intermediate(中間操作)和Terminal(終止操作)。中間操作的返回結果都是Stream,故可以多個中間操作疊加;終止操作用于返回我們最終需要的數據,只能有一個終止操作。至于哪些方法是中間操作,哪些方法是終止操作,我們一會兒再說。
使用Stream流,可以清楚地知道我們要對一個數據集做何種操作,可讀性強。而且可以很輕松地獲取并行化Stream流,不用自己編寫多線程代碼,可以讓我們更加專注于業務邏輯。
默認情況下,從有序集合、生成器、迭代器產生的流或者通過調用Stream.sorted產生的流都是有序流,有序流在并行處理時會在處理完成之后恢復原順序。unordered()方法可以解除有序流的順序限制,更好地發揮并行處理的性能優勢,例如distinct將保存任意一個唯一元素而不是第一個,limit將保留任意n個元素而不是前n個。
流的常用生成方法
Collection接口的stream()或parallelStream()方法
parallelStream()是并行方法,parallelStream()不一定比stream()快(需要開啟線程,線程競爭),而且parallelStream()是線程不安全的,所以使用parallelStream()要綜合考量,測試百萬數據的List<String> 的System.out.println(),stream()依舊比parallelStream()要快
List<Integer> list = new ArrayList();Random random = new Random();for (int i = 0; i < 100000; i++) {list.add(random.nextInt());}Instant start = Instant.now();
// list.stream().forEach(System.out::print); //193毫秒list.parallelStream().forEach(System.out::print);//275毫秒Instant end = Instant.now();System.out.println("");System.out.println("_______________________________________________________________________");System.out.println("end:" + end.toEpochMilli());System.out.println("start:" + start.toEpochMilli());System.out.println("time:" + (end.toEpochMilli() - start.toEpochMilli()));
靜態的Stream.of()、Stream.empty()方法
List<Integer> list = new ArrayList();Random random = new Random();for (int i = 0; i < 100000; i++) {list.add(random.nextInt());}Instant start = Instant.now();
// Stream.of(list).forEach(System.out::println); //71毫秒Stream.empty().forEach(System.out::println);//37毫秒
// list.stream().forEach(System.out::print); //193毫秒
// list.parallelStream().forEach(System.out::print);//275毫秒Instant end = Instant.now();System.out.println("");System.out.println("_______________________________________________________________________");System.out.println("end:" + end.toEpochMilli());System.out.println("start:" + start.toEpochMilli());System.out.println("time:" + (end.toEpochMilli() - start.toEpochMilli()));
Arrays.stream(array, from, to)
包前不包后
int[] arr = {1, 3, 2, 4, 55, 64, 98, 23};Arrays.stream(arr, 1, 3).forEach(System.out::println);
流的常用Intermediate方法(中間操作)
filter(Predicate) 將結果為false的元素過濾掉
int[] arr = {1, 3, 2, 4, 55, 64, 98, 23};//過濾單數保留雙數Arrays.stream(arr).filter(item->(item & 1) == 0).forEach(System.out::println);
map(fun) 轉換元素的值,可以用方法引元或者lambda表達式
int[] arr = {1, 3, 2, 4, 55, 64, 98, 23};//filter過濾單數保留雙數//map將剩余元素除以2Arrays.stream(arr).filter(item->(item & 1) == 0).map(item -> item / 2).forEach(System.out::println);
flatMap(fun) 若元素是流,將流攤平為正常元素,再進行元素轉換
簡單點描述就是嵌套的集合扁平化
比如集合里面包含集合/數組?
List<List<String>> list = Arrays.asList(Arrays.asList("A", "B"),Arrays.asList("C", "D"),Arrays.asList("E", "E"));// 使用 flatMap 扁平化列表List<String> collect = list.stream().flatMap(List::stream).collect(Collectors.toList());System.out.println(collect); //[A, B, C, D, E, E]
或者集合里面本身沒有集合/數組但是可以操作(字符串切割)變成集合/數組
List<Person> list = Arrays.asList(new Person("張三", "1,2,3"),new Person("李四", "2,3"),new Person("王五", "2,4"));Set<Object> collect = list.stream().flatMap(person -> Arrays.stream(person.getHobbies().split(","))).collect(Collectors.toSet());System.out.println(collect);
limit(n) 保留前n個元素 常配合排序使用
int[] arr = {1, 3, 2, 4, 55, 64, 98, 23};Arrays.stream(arr).limit(2).forEach(System.out::print); //13
skip(n) 跳過前n個元素
int[] arr = {1, 3, 2, 4, 55, 64, 98, 23};Arrays.stream(arr).skip(6).forEach(System.out::print); //9823
distinct() 剔除重復元素
int[] arr = {1, 2, 2, 4, 2};Arrays.stream(arr).distinct().forEach(System.out::print); //124
sorted() ,sorted(Comparator) 將Comparable元素的流排序
int[] arr = {1, 2, 2, 4, 2};Arrays.stream(arr).sorted().forEach(System.out::print); //12224
List<Person> list = Arrays.asList(new Person("張三", 18,"2,3"),new Person("李四", 14,"2,3"),new Person("王五", 21,"2,4"));//根據年齡排序 reversed()倒序操作list.stream().sorted(Comparator.comparing(Person::getAge).reversed()).forEach(System.out::println);
peek(fun) 流不變,但會把每個元素傳入fun執行,可以用作調試
可以理解為沒有返回結果的forEach()
List<Person> list = Arrays.asList(new Person("張三", 18,"2,3"),new Person("李四", 14,"2,3"),new Person("王五", 21,"2,4"));//根據年齡排序 reversed()倒序操作list.stream().sorted(Comparator.comparing(Person::getAge).reversed()).peek(item->{item.setAge(item.getAge() + 1);}).peek(item->{item.setAge(item.getAge() + 1);}).peek(item->{item.setAge(item.getAge() + 1);}).forEach(System.out::println);結果:
Person(name=王五, age=24, hobbies=2,4)
Person(name=張三, age=21, hobbies=2,3)
Person(name=李四, age=17, hobbies=2,3)
流的Terminal方法(終結操作)
max(Comparator)
min(Comparator)
count()
findFirst() 返回第一個元素
findAny() 返回任意元素
anyMatch(Predicate) ,allMatch(Predicate) ,noneMatch(Predicate)
anyMatch(Predicate)任意元素匹配時返回true
所有元素匹配時返回true
沒有元素匹配時返回true