Java8新特性之流式操作


什么是流式操作

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

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末蛮浑,一起剝皮案震驚了整個濱河市唠叛,隨后出現的幾起案子,更是在濱河造成了極大的恐慌沮稚,老刑警劉巖艺沼,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異蕴掏,居然都是意外死亡障般,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進店門盛杰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來挽荡,“玉大人,你說我怎么就攤上這事即供《猓” “怎么了?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵逗嫡,是天一觀的道長青自。 經常有香客問我株依,道長,這世上最難降的妖魔是什么延窜? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任恋腕,我火速辦了婚禮,結果婚禮上逆瑞,老公的妹妹穿的比我還像新娘荠藤。我一直安慰自己,他們只是感情好呆万,可當我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布商源。 她就那樣靜靜地躺著,像睡著了一般谋减。 火紅的嫁衣襯著肌膚如雪牡彻。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天出爹,我揣著相機與錄音庄吼,去河邊找鬼。 笑死严就,一個胖子當著我的面吹牛总寻,可吹牛的內容都是我干的。 我是一名探鬼主播梢为,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼渐行,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了铸董?” 一聲冷哼從身側響起祟印,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎粟害,沒想到半個月后蕴忆,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡悲幅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年套鹅,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片汰具。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡卓鹿,死狀恐怖,靈堂內的尸體忽然破棺而出郁副,到底是詐尸還是另有隱情减牺,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站拔疚,受9級特大地震影響肥隆,放射性物質發(fā)生泄漏。R本人自食惡果不足惜稚失,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一栋艳、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧句各,春花似錦吸占、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至初厚,卻和暖如春件蚕,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背产禾。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工排作, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人亚情。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓妄痪,卻偏偏與公主長得像,于是被迫代替她去往敵國和親楞件。 傳聞我的和親對象是個殘疾皇子衫生,可洞房花燭夜當晚...
    茶點故事閱讀 44,700評論 2 354

推薦閱讀更多精彩內容

  • 1. 什么是λ表達式 λ表達式本質上是一個匿名方法。讓我們來看下面這個例子: public int add(int...
    黎景陽閱讀 2,157評論 1 3
  • Java 8自Java 5(發(fā)行于2004)以來最具革命性的版本土浸。Java 8 為Java語言障簿、編譯器、類庫栅迄、開發(fā)...
    huoyl0410閱讀 627評論 1 2
  • Java 8自Java 5(發(fā)行于2004)以來最具革命性的版本。Java 8 為Java語言皆怕、編譯器毅舆、類庫、開發(fā)...
    誰在烽煙彼岸閱讀 888評論 0 4
  • 轉自: Java 8 中的 Streams API 詳解 為什么需要 Stream Stream 作為 Java ...
    普度眾生的面癱青年閱讀 2,918評論 0 11
  • DAY 05 1愈腾、 public classArrayDemo { public static void mai...
    周書達閱讀 661評論 0 0