什么是流式操作
Java 8 API添加了一個新的抽象稱為流Stream,可以讓你以一種聲明的方式處理數據。
Stream 使用一種類似用 SQL 語句從數據庫查詢數據的直觀方式來提供一種對 Java 集合運算和表達的高階抽象缩搅。
Stream API可以極大提高Java程序員的生產力溉知,讓程序員寫出高效率、干凈刃宵、簡潔的代碼。
這種風格將要處理的元素集合看作一種流徘公, 流在管道中傳輸牲证, 并且可以在管道的節(jié)點上進行處理, 比如篩選关面, 排序坦袍,聚合等。
元素流在管道中經過中間操作(intermediate operation)的處理等太,最后由最終操作(terminal operation)得到前面處理的結果捂齐。
1.流式操作舉例
1.1創(chuàng)建實體類
public class Person {
? ? private String name;
? ? private Integer age;
? ? private Integer score;
? ? public String getName() {
? ? ? ? return name;
? ? }
? ? public void setName(String name) {
? ? ? ? this.name = name;
? ? }
? ? public Integer getAge() {
? ? ? ? return age;
? ? }
? ? public void setAge(Integer age) {
? ? ? ? this.age = age;
? ? }
? ? public Integer getScore() {
? ? ? ? return score;
? ? }
? ? public void setScore(Integer score) {
? ? ? ? this.score = score;
? ? }
? ? public Person() {
? ? }
? ? public Person(String name, Integer age, Integer score) {
? ? ? ? this.name = name;
? ? ? ? this.age = age;
? ? ? ? this.score = score;
? ? }
? ? @Override
? ? public String toString() {
? ? ? ? return "Person{" +
? ? ? ? ? ? ? ? "name='" + name + '\'' +
? ? ? ? ? ? ? ? ", age=" + age +
? ? ? ? ? ? ? ? ", score=" + score +
? ? ? ? ? ? ? ? '}';
? ? }
}
1.2 傳統(tǒng)的對象初始化方式
public class Program {
? ? public static void main(String[] args) {
? ? ? ? //使用構造器設置對象信息
//? ? ? ? Person xiaomign = new Person("小明", 28, 90);
? ? ? ? //使用getter、setter方式設置對象信息
? ? ? ? Person xiaoming = new Person();
? ? ? ? xiaoming.setName("小明");
? ? ? ? xiaoming.setAge(18);
? ? ? ? xiaoming.setScore(90);
? ? }
}
1.3 使用流式操作初始化對象
1.3.1 修改實體類
public class Person {
? ? private String name;
? ? private Integer age;
? ? private Integer score;
? ? public String getName() {
? ? ? ? return name;
? ? }
? ? public Person setName(String name) {
? ? ? ? this.name = name;
? ? ? ? return this;
? ? }
? ? public Integer getAge() {
? ? ? ? return age;
? ? }
? ? public Person setAge(Integer age) {
? ? ? ? this.age = age;
? ? ? ? return this;
? ? }
? ? public Integer getScore() {
? ? ? ? return score;
? ? }
? ? public Person setScore(Integer score) {
? ? ? ? this.score = score;
? ? ? ? return this;
? ? }
? ? public Person() {
? ? }
? ? public Person(String name, Integer age, Integer score) {
? ? ? ? this.name = name;
? ? ? ? this.age = age;
? ? ? ? this.score = score;
? ? }
? ? @Override
? ? public String toString() {
? ? ? ? return "Person{" +
? ? ? ? ? ? ? ? "name='" + name + '\'' +
? ? ? ? ? ? ? ? ", age=" + age +
? ? ? ? ? ? ? ? ", score=" + score +
? ? ? ? ? ? ? ? '}';
? ? }
}
1.3.2 使用流式操作
//流式操作
xiaoming.setName("小明").setAge(20).setScore(100);
2.集合的流式操作
集合的流式操作是Java8的一個新特性缩抡,流式操作不是一個數據結構奠宜,不負責任何的數據存儲,它更像是一個迭代器瞻想,可以有序的獲取數據源中的每一個數據压真,并且可以對這些數據進行一些操作。流式操作的每一個方法的返回值都是這個流的本身蘑险。
2.1 流式操作的三個步驟
2.1.1 獲取數據源:集合滴肿、數組
設置數據源
public class Data {
? ? /**
? ? * 數據源
? ? */
? ? public static ArrayList<Person> getData() {
? ? ? ? ArrayList<Person> list = new ArrayList<Person>();
? ? ? ? list.add(new Person("小明", 18, 100));
? ? ? ? list.add(new Person("小麗", 19, 70));
? ? ? ? list.add(new Person("小王", 22, 85));
? ? ? ? list.add(new Person("小張", 20, 90));
? ? ? ? list.add(new Person("小黑", 21, 95));
? ? ? ? return list;
? ? }
}
獲取數據源的方式
public class Program {
? ? public static void main(String[] args) {
? ? ? ? // 獲取數據源方式1
? ? ? ? Stream stream = Data.getData().stream();
? ? ? ? // 獲取數據源方式2
? ? ? ? Stream.of(Data.getData());
? ? ? ? // 獲取數據源方式3
? ? ? ? //數據源為數組
? ? }
}
2.1.2 對數據進行處理的過程:過濾、排序漠其、映射等(中間操作)
中間操作1:filter
使用filter自定義條件過濾數據
// 中間操作1: filter
// filter是一個過濾器嘴高,可以自定義一個過濾條件,將流中滿足條件的元素保留
// 查找集合中成績小于80的學生
List<Person> list = Data.getData().stream()
? ? .filter(ele -> ele.getScore() < 80)
? ? .collect(Collectors.toList());
System.out.println(list);
中間操作2:distinct
使用distinct實現去重操作
在數據源中添加重復的數據
list.add(new Person("小黑", 21, 95)); //此時list中有兩個小黑
在實體類中重寫hashCode()和equals()方法
@Override
public boolean equals(Object o) {
? ? if (this == o) return true;
? ? if (o == null || getClass() != o.getClass()) return false;
? ? Person person = (Person) o;
? ? return Objects.equals(name, person.name) &&
? ? ? ? Objects.equals(age, person.age) &&
? ? ? ? Objects.equals(score, person.score);
}
@Override
public int hashCode() {
? ? return Objects.hash(name, age, score);
}
去重規(guī)則:
先判斷對象的hashCode()
如果hashCode()相同再判斷equals()
// 中間操作2: distinct
// distinct: 取出集合中不同的元素
// 去重規(guī)則:
// 1.先判斷對象的hashCode()
// 2.如果hashCode()相同再判斷equals()
Data.getData().stream().distinct().forEach(System.out::println);
注意:如果小黑的數據相同卻要保存兩份和屎,可以在hashCode()方法中返回一個隨機數拴驮,隨機數很小概率會相同,為了確保穩(wěn)定性柴信,可以將equals()方法改為返回false套啤,這樣可以保留兩個信息相同的小黑。
中間操作3:sorted
使用sorted()方法以成績進行升序排序
要求實體類實現Comparable接口并重寫方法
// 中間操作3: sorted
// sorted: 對返回的元素進行排序
// sorted(): 要求實體類實現Comparable接口并重寫方法
Data.getData().stream().sorted().forEach(System.out::println);
中間操作4:limit
在數據源中取前三個數據
// 中間操作4: limit
// limit: 限制,只取流中前指定位的數據
// 在數據源中取前三個數據
Data.getData().stream().limit(3).forEach(System.out::println);
中間操作5:skip
// 中間操作5: skip
// skip: 跳過
// 跳過前三個元素,取后面剩下的元素
Data.getData().stream().skip(3).forEach(System.out::println);
中間操作6:map
元素映射,用指定的元素替換掉流中的元素
使用map將對象替換為對象的名字
// 中間操作6: map
// map: 元素映射,用指定的元素替換掉流中的元素
// 將流中的Person對象替換位他們的姓名
Data.getData().stream().map(ele -> ele.getName()).forEach(System.out::println);
2.1.3 對流中數據的整合:轉成集合、數量(最終操作)
最終操作1:collect
轉換為List
public class Program {
? ? public static void main(String[] args) {
? ? ? ? // 獲取數據源方式1
? ? ? ? Stream<Person> stream = Data.getData().stream();
? ? ? ? // 最終操作1: collect潜沦,配合Collectors使用
? ? ? ? // 將集合中的元素轉換成List
? ? ? ? List<Person> list = stream.collect(Collectors.toList());
? ? ? ? System.out.println(list);
? ? }
}
運行結果
轉換為set
// 將集合中的元素轉換為Set
Set<Person> set = stream.collect(Collectors.toSet());
System.out.println(set);
運行結果
轉換為map
// 轉換為Map(name為鍵萄涯,score為值)
? ? ? ? // 方式1
//? ? ? ? Map<String, Integer> map = stream.collect(Collectors.toMap(
//? ? ? ? ? ? ? ? ele -> ele.getName(),
//? ? ? ? ? ? ? ? ele -> ele.getScore()
//? ? ? ? ));?
? ? ? ? // 方式2? ? ? ?
? ? ? ? Map<String, Integer> map = stream.collect(Collectors.toMap(
? ? ? ? ? ? ? ? Person::getName,
? ? ? ? ? ? ? ? Person::getScore
? ? ? ? ));
運行結果
最終操作2:reduce
reduce的思想
比如在計算一個數組中的元素的和時,首先會計算前兩個數的和唆鸡,然后拿著前兩個數的和與第三個數求和涝影,計算出結果后將三個數的和與第四個數相加,以此類推争占。
計算數組中數據的和
// 最終操作2: reduce(將數據匯總在一起)
Stream<Integer> stream1 = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Optional<Integer> res = stream1.reduce((n1, n2) -> n1 + n2);
// 獲取到最終的返回值
System.out.println(res.get());
使用reduce計算Person對象中成績的和
// 計算Person中Score的和
Optional<Person> res = stream.reduce(
? ? (n1, n2) -> new Person().setScore(n1.getScore() + n2.getScore())
);
System.out.println(res.get().getScore());
缺點:上面的寫法每次都會產生一個臨時的對象燃逻,產生了不必要的性能損耗
使用reduce計算Person對象中成績的和(優(yōu)化)
// 計算Person中Score的和(使用臨時變量,減少性能開銷)
Person temp = new Person();
Optional<Person> res = stream.reduce(
? ? (n1, n2) -> temp.setScore(n1.getScore() + n2.getScore())
);
System.out.println(res.get().getScore());最終操作3:max和min
使用max找出Person中成績最高的人
// 最終操作3: max和min
// 需求1: 找到集合中成績最高的人的信息
Person max = stream.max(
? ? (ele1, ele2) -> ele1.getScore() - ele2.getScore()
).get();
System.out.println(max);
使用min找出Person中成績最低的人
// 需求2: 找到集合中成績最低的人的信息
Person min = stream.min(
? ? (ele1, ele2) -> ele1.getScore() - ele2.getScore()
).get();
System.out.println(min);
最終操作4:matching
使用anyMatch查看集合中是否有成績高于80的人
// 判斷集合中是否包含成績大于80的學員
boolean res1 = stream.anyMatch((ele) -> ele.getScore() > 80);
System.out.println(res1);
使用allMatch查看集合中的成績是否全部高于60
//查看集合中的人的成績是否全部高于60
boolean res2 = stream.allMatch((ele) -> ele.getScore() > 60);
System.out.println(res2);
使用noneMatch查看集合中的人的分數是否不包含80以下的
boolean res3 = stream.noneMatch((ele) -> ele.getScore() < 80);
System.out.println(res3);
最終操作5:count
使用count計算元數據中有多少條數據
// 最終操作5: 求元數據中有多少個元素
long count = stream.count();
System.out.println(count);
最終操作6:forEach
使用forEach遍歷集合中的元素
// 最終操作6: forEach
// stream.forEach(ele -> System.out.println(ele));
stream.forEach(System.out::println);
最終操作7:findFirst和findAny FindFirst: 獲取流中的第一個元素FindAny: 獲取流中任意一個元素(并不是隨機獲取元素)對于串行流臂痕,結果等同于findFirst findAny用于并行流中可能會與findFirst一樣伯襟,也可能不一樣
// FindFirst: 獲取流中的第一個元素
// FindAny: 獲取流中任意一個元素(并不是隨機獲取元素)
//? ? ? ? ? 對于串行流,結果等同于findFirst
//? ? ? ? ? findAny用于并行流中可能會與findFirst一樣握童,也可能不一樣
System.out.println(Data.getData().parallelStream().findFirst());
System.out.println(Data.getData().stream().findFirst());
System.out.println(Data.getData().parallelStream().findAny());
System.out.println(Data.getData().stream().findAny());
最終操作的注意事項
為什么會被稱為最終操作?
Person max = stream.max(
? ? (ele1, ele2) -> ele1.getScore() - ele2.getScore()
).get();
Person min = stream.min(
? ? (ele1, ele2) -> ele1.getScore() - ele2.getScore()
).get();
報錯信息表示流正在被處理或者已經被關閉了姆怪,如果已經被關閉了再次調用當然會報錯,這也是為什么叫最終操作的原因澡绩。
3.并行流
3.1 獲取并行流的方式
// 并行流
// 獲取并行流的兩種方式
Data.getData().stream().parallel();
Data.getData().parallelStream();
3.2 并行流與串行流對比
// 串行流: 19920ms
// 并行流: 12204ms
long startTime = System.currentTimeMillis();
//LongStream.rangeClosed(0L, 50000000000L)
//? ? .reduce(Long::sum);
LongStream.rangeClosed(0L, 50000000000L)
? ? .parallel()
? ? .reduce(Long::sum);
long endTime = System.currentTimeMillis();
System.out.println(endTime - startTime);
3.3 flatMap
String[] array = {"hello", "world"};
// 需要獲取所有字符 List -> h, e, l, l, o, w, o, r, l, d
//? ? ? ? Arrays.stream(array)
//? ? ? ? ? ? ? ? .map(ele -> ele.split(""))
//? ? ? ? ? ? ? ? .forEach(ele -> System.out.println(ele.length));
System.out.println(Arrays.stream(array)
? ? ? ? ? ? ? ? ? .map(ele -> ele.split(""))
? ? ? ? ? ? ? ? ? .flatMap(Arrays::stream)
? ? ? ? ? ? ? ? ? .collect(Collectors.toList()));
4.Collectors
Collectors是一個工具類稽揭,提供著若干個方法,返回一個Collector接口的實現類對象
4.1 maxBy
? 通過指定的規(guī)則獲取流中最大的元素
System.out.println(Data.getData().stream()
? ? ? ? ? ? ? ? .collect(Collectors.maxBy((ele1, ele2) -> ele1.getScore() - ele2.getScore())));
4.2 minBy
? 通過指定的規(guī)則獲取流中最小的元素
System.out.println(Data.getData().stream()
? ? ? ? ? ? ? ? .collect(Collectors.minBy((ele1, ele2) -> ele1.getScore() - ele2.getScore())));
4.3 joining
合并英古,將流中的元素淀衣,以字符串的形式拼接起來
// 把Person中的姓名拼成一個字符串
String res1 = Data.getData().stream()
? ? .map(Person::getName)
? ? .collect(Collectors.joining());
System.out.println(res1);
String res2 = Data.getData().stream()
? ? .map(Person::getName)
? ? .collect(Collectors.joining("-"));
System.out.println(res2);
String res3 = Data.getData().stream()
? ? .map(Person::getName)
? ? .collect(Collectors.joining("-", "{", "}"));
System.out.println(res3);
4.4 summingInt
計算int類型的和,將流中的元素映射為int類型的元素進行求和
將Person對象的成績進行求和
// 將Person對象的成績進行求和
System.out.println(Data.getData().stream()
? ? ? ? ? ? ? ? ? .collect(Collectors.summingInt(ele -> ele.getScore())));
4.5 averagingInt
計算int類型的平均值
計算不及格學生的平均成績
System.out.println(Data.getData().stream()
? ? ? ? ? ? ? ? ? .filter(ele -> ele.getScore() < 60)
? ? ? ? ? ? ? ? ? .collect(Collectors.averagingInt(Person::getScore)));
4.6 summarizingInt
將流中的元素映射成int類型的元素召调,獲取這些數據的描述信息
System.out.println(Data.getData().stream()
? ? ? ? ? ? ? ? ? .collect(Collectors.summarizingInt(ele -> ele.getScore())));
想獲得 更多的學習資料 在評論下方評論【學習】就可以了哦
鏈接:https://juejin.im/post/5e15e9fc5188253a9b1bf858
。