Java 8 API添加了一個新的抽象稱爲流Stream,可讓你以一種聲明的方式處理數據。java
Stream 使用一種相似用 SQL 語句從數據庫查詢數據的直觀方式來提供一種對 Java 集合運算和表達的高階抽象。程序員
Stream API能夠極大提升Java程序員的生產力,讓程序員寫出高效率、乾淨、簡潔的代碼。數據庫
這種風格將要處理的元素集合看做一種流, 流在管道中傳輸, 而且能夠在管道的節點上進行處理, 好比篩選, 排序,聚合等。數組
元素流在管道中通過中間操做(intermediate operation)的處理,最後由最終操做(terminal operation)獲得前面處理的結果。數據結構
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 +
'}';
}
}
複製代碼
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);
}
}
複製代碼
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 +
'}';
}
}
複製代碼
//流式操做
xiaoming.setName("小明").setAge(20).setScore(100);
複製代碼
集合的流式操做是Java8的一個新特性,流式操做不是一個數據結構,不負責任何的數據存儲,它更像是一個迭代器,能夠有序的獲取數據源中的每個數據,而且能夠對這些數據進行一些操做。流式操做的每個方法的返回值都是這個流的自己。ide
設置數據源工具
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
//數據源爲數組
}
}
複製代碼
使用filter自定義條件過濾數據優化
// 中間操做1: filter
// filter是一個過濾器,能夠自定義一個過濾條件,將流中知足條件的元素保留
// 查找集合中成績小於80的學生
List<Person> list = Data.getData().stream()
.filter(ele -> ele.getScore() < 80)
.collect(Collectors.toList());
System.out.println(list);
複製代碼
使用distinct實現去重操做this
在數據源中添加劇復的數據
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);
}
複製代碼
去重規則:
先判斷對象的hashCode()
若是hashCode()相同再判斷equals()
// 中間操做2: distinct
// distinct: 取出集合中不一樣的元素
// 去重規則:
// 1.先判斷對象的hashCode()
// 2.若是hashCode()相同再判斷equals()
Data.getData().stream().distinct().forEach(System.out::println);
複製代碼
注意:若是小黑的數據相同卻要保存兩份,能夠在hashCode()
方法中返回一個隨機數,隨機數很小几率會相同,爲了確保穩定性,能夠將equals()
方法改成返回false
,這樣能夠保留兩個信息相同的小黑。
使用sorted()
方法以成績進行升序排序
要求實體類實現Comparable接口並重寫方法
// 中間操做3: sorted
// sorted: 對返回的元素進行排序
// sorted(): 要求實體類實現Comparable接口並重寫方法
Data.getData().stream().sorted().forEach(System.out::println);
複製代碼
在數據源中取前三個數據
// 中間操做4: limit
// limit: 限制,只取流中前指定位的數據
// 在數據源中取前三個數據
Data.getData().stream().limit(3).forEach(System.out::println);
複製代碼
跳過前三個元素,取後面剩下的元素
// 中間操做5: skip
// skip: 跳過
// 跳過前三個元素,取後面剩下的元素
Data.getData().stream().skip(3).forEach(System.out::println);
複製代碼
元素映射,用指定的元素替換掉流中的元素
使用map將對象替換爲對象的名字
// 中間操做6: map
// map: 元素映射,用指定的元素替換掉流中的元素
// 將流中的Person對象替換位他們的姓名
Data.getData().stream().map(ele -> ele.getName()).forEach(System.out::println);
複製代碼
轉換爲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
));
複製代碼
運行結果
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對象中成績的和(優化)
// 計算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());
複製代碼
使用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);
複製代碼
使用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);
複製代碼
使用count計算元數據中有多少條數據
// 最終操做5: 求元數據中有多少個元素
long count = stream.count();
System.out.println(count);
複製代碼
使用forEach遍歷集合中的元素
// 最終操做6: forEach
// stream.forEach(ele -> System.out.println(ele));
stream.forEach(System.out::println);
複製代碼
// 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();
複製代碼
報錯信息表示流正在被處理或者已經被關閉了,若是已經被關閉了再次調用固然會報錯,這也是爲何叫最終操做的緣由。
// 並行流
// 獲取並行流的兩種方式
Data.getData().stream().parallel();
Data.getData().parallelStream();
複製代碼
// 串行流: 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);
複製代碼
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()));
複製代碼
Collectors是一個工具類,提供着若干個方法,返回一個Collector接口的實現類對象
經過指定的規則獲取流中最大的元素
System.out.println(Data.getData().stream()
.collect(Collectors.maxBy((ele1, ele2) -> ele1.getScore() - ele2.getScore())));
複製代碼
經過指定的規則獲取流中最小的元素
System.out.println(Data.getData().stream()
.collect(Collectors.minBy((ele1, ele2) -> ele1.getScore() - ele2.getScore())));
複製代碼
合併,將流中的元素,以字符串的形式拼接起來
// 把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);
複製代碼
計算int類型的和,將流中的元素映射爲int類型的元素進行求和
將Person對象的成績進行求和
// 將Person對象的成績進行求和
System.out.println(Data.getData().stream()
.collect(Collectors.summingInt(ele -> ele.getScore())));
複製代碼
計算int類型的平均值
計算不及格學生的平均成績
System.out.println(Data.getData().stream()
.filter(ele -> ele.getScore() < 60)
.collect(Collectors.averagingInt(Person::getScore)));
複製代碼
將流中的元素映射成int類型的元素,獲取這些數據的描述信息
System.out.println(Data.getData().stream()
.collect(Collectors.summarizingInt(ele -> ele.getScore())));
複製代碼