目錄
一、流的概述
定義:
特點:
pipelining:很多流的操作也是返回一個流
Internal Iteration:流操作自動進行迭代,用戶感知不到循環遍歷。
工作流程:
- 流的創建
- 流轉換爲其他流的中間操作,可以包括多個步驟(惰性步驟)
- 流的計算結果。這個操作會強制執行之前的惰性操作。這個步驟以後,流就再也不能用了
示例:將grocery訂單按金額從大到小輸出id.
import java.util.*;
import java.util.stream.Collectors;
public class StreamDemo {
public static void main(String[] args) {
List<Transaction> transactions = new ArrayList<Transaction>();
transactions.add(new Transaction(1, 100, "batch"));
transactions.add(new Transaction(3, 80, "grocery"));
transactions.add(new Transaction(6, 120, "grocery"));
transactions.add(new Transaction(7, 40, "batch"));
transactions.add(new Transaction(10, 50, "grocery"));
// 採用傳統方法
traditionalMethod(transactions);
System.out.print("============\n");
//流方法
streamMethod(transactions);
}
public static void traditionalMethod(List<Transaction> transactions) {
// 過濾保留type = "grocery"的記錄
List<Transaction> groceryTransactions = new ArrayList<>();
for (Transaction t : transactions) {
if (t.getType().equals("grocery")) {
groceryTransactions.add(t);
}
}
// 根據value對符合的記錄排序,從高到低
// Collections.sort(groceryTransactions, new Comparator<Transaction>() {
// public int compare(Transaction t1, Transaction t2) {
// return t2.getValue().compareTo(t1.getValue());
// }
// });
//利用Lambda表達式
Collections.sort(groceryTransactions, (t1,t2) ->t2.getValue().compareTo(t1.getValue()));
// 獲取記錄中的id字段
List<Integer> transactionIds = new ArrayList<>();
for (Transaction t : groceryTransactions) {
transactionIds.add(t.getId());
}
// 輸出結果
transactionIds.forEach(System.out::println);
}
//流技術的運用
public static void streamMethod(List<Transaction> transactions) {
transactions.stream().filter(t->t.getType().equals("grocery"))
.sorted(Comparator.comparing(Transaction::getValue).reversed())
.map(Transaction::getId)
.collect(Collectors.toList())
.forEach(System.out::println);
}
}
class Transaction {
int id;
Integer value;
String type;
public Transaction(int id, int value, String type) {
super();
this.id = id;
this.value = value;
this.type = type;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Integer getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
輸出:
6
3
10
============
6
3
10
二、流的創建
1.Collection接口的stream方法,將其轉換
// Collection子類產生stream
Stream<String> a1 = new ArrayList<String>().stream();
Stream<String> a2 = new HashSet<String>().stream();
// 使用Arrays.stream 轉化數組爲stream
Stream<String> b1 = Arrays.stream("a,b,c,d,e".split(","), 3, 5);
2.用Stream類進行轉化:
- of方法將數組轉化,
- empty方法產生一個空流,
- generate方法接收Lambda表達式,
- iterate方法接收一個種子和Lambda表達式。
// 數組產生stream
Stream<Integer> c1 = Stream.of(new Integer[5]);
Stream<String> c2 = Stream.of("a,b,c".split(","));
Stream<String> c3 = Stream.of("a", "b", "c");
//空流
Stream<String> d1 = Stream.empty();
//無限流,使用generate方法,根據Lambda表達式產生
Stream<String> e1 = Stream.generate(()->"hello");
Stream<Double> e2 = Stream.generate(Math::random);
//無限流,使用iterate方法,第一個參數是種子
//第二個是Lambda表達式
Stream<BigInteger> e3 = Stream.iterate(BigInteger.ZERO, n -> n.add(BigInteger.ONE));
其他創建流的方法
//Files的lines方法讀取一個文件,產生每一行內容的Stream
Stream<String> contents = Files.lines(Paths.get("C:/abc.txt"));
//Pattern的splitAsStream方法,根據一個正則表達式,將內容分爲一個字符串的Stream
Stream<String> words = Pattern.compile(",").splitAsStream("a,b,c");
3.基本類型流
只有IntStream,LongStream,DoubleStream
IntStream s1 = IntStream.of(1,2,3,4,5);
s1 = Arrays.stream(new int[] {1,2,3});
s1 = IntStream.generate(()->(int)(Math.random() * 100));
s1 = IntStream.range(1,5); //1,2,3,4 step 1
s1 = IntStream.rangeClosed(1,5); //1,2,3,4,5
4.基本類型流和對象流的轉換
IntStream s2 = IntStream.of(1,2,3,4,5);
Stream<Integer> s3 = s2.boxed();//轉換爲對象流
IntStream s5 = s3.mapToInt(Integer::intValue);//對象流轉換爲基本流
5.並行流的創建
三、流的轉換
有這麼幾類操作:過濾、去重、排序、轉化、抽取/跳過/連接、其他
1.過濾:
public class StreamFilter {
public static void main(String[] args) {
System.out.println("======對每個元素進行過濾判定================");
Stream<Integer> s1 = Stream.of(1, 2, 3, 4, 5);
Stream<Integer> s2 = s1.filter(n -> n>2);
s2.forEach(System.out::println);
//3, 4, 5
}
}
2.去重distinct();
對流元素進行過濾,去除重複,只留下不重複的。
當去重對象時,會調用hashCoode再調用equals方法進行判重
import java.util.ArrayList;
import java.util.stream.Stream;
public class StreamDistinct {
public static void main(String[] args) {
System.out.println("======基本類型包裝類對象去重================");
Stream<Integer> s1 = Stream.of(1, 1, 2, 2, 3, 3);
Stream<Integer> s2 = s1.distinct();
s2.forEach(System.out::println);
// 1, 2, 3
System.out.println("======自定義對象的去重================");
ArrayList<Student> students = new ArrayList<Student>();
students.add(new Student("Tom", 20));
students.add(new Student("Tom", 20));
students.add(new Student("Jerry", 20));
students.add(new Student("Jerry", 18));
// 先對象的hashCode再調用equals方法進行判重
Stream<Student> s3 = students.stream().distinct();
s3.forEach(System.out::println);
}
}
class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int hashCode() {
return name.hashCode() * 1000 + age;
}
@Override
public boolean equals(Object o) {
Student s = (Student) o;
if ((this.age == s.age)
&& this.name.equals(s.name)) {
return true;
} else {
return false;
}
}
public String toString() {
return "name:" + name + ", age:" + age;
}
}
3.排序
sort()方法:對流的基本類型包裝類元素進行排序
可提供Comparator,對流進行排序
可在流的自定義對象進行排序,對象內實現compareTo方法
import java.util.ArrayList;
import java.util.Comparator;
import java.util.stream.Stream;
public class StreamOrder {
public static void main(String[] args) {
System.out.println("=======對基本類型包裝類對象進行排序======");
Stream<Integer> s1 = Stream.of(3,2,4,1,5);
Stream<Integer> s2 = s1.sorted();
s2.forEach(System.out::println);
//1, 2, 3, 4, 5
System.out.println("=======提供Comparator進行排序===============");
String[] planets = new String[] {
"Mercury", "Venus", "Earth",
"Mars", "Jupiter", "Saturn",
"Uranus", "Neptune" };
Stream<String> s3 = Stream.of(planets).sorted(
Comparator.comparing(String::length));
s3.forEach(System.out::println);
System.out.println("========對自定義對象進行排序==============");
ArrayList<Cat> cats = new ArrayList<>();
cats.add(new Cat(3));
cats.add(new Cat(2));
cats.add(new Cat(5));
cats.add(new Cat(1));
cats.add(new Cat(4));
Stream<Cat> s4 = cats.stream().sorted();
s4.forEach(System.out::println);
}
}
class Cat implements Comparable<Cat> {
private int size;
public Cat(int size) {
super();
this.size = size;
}
@Override
public int compareTo(Cat o) {
Cat c = new Cat(5);
System.out.println(c.size);
return this.size - o.size;
}
public String toString()
{
return "Size:" + size;
}
}
4.轉化map:
- 利用方法引用,對流的每一個元素進行函數計算。
- 利用Lambda表達式,對流的每一個元素進行計算。
- 利用方法引用,對流的每一個元素進行函數計算返回Stream
- 利用方法引用,對流的每一個元素進行函數計算返回Stream,併合並所有結果
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
public class StreamMap {
public static void main(String[] args) {
System.out.println("======用方法引用對每個元素進行計算=====");
//map使用方法引用,輸入一個參數,返回一個結果
Stream<Double> s1 = Stream.of(-1.5, 2.5, -3.5);
Stream<Double> s2 = s1.map(Math::abs);
s2.forEach(System.out::println);
System.out.println("======用Lambda表達式對每個元素進行計算=");
//map使用Lambda表達式,輸入一個參數,返回一個結果
Stream<Integer> s3 = Stream.of(1,2,3,4,5);
Stream<Integer> s4 = s3.map(n->n*n);
s4.forEach(System.out::println);
System.out.println("======對每個元素進行計算,返回Stream==");
//map使用方法引用,輸入一個參數,返回一個Stream
String[] planets = new String[] {
"Mercury", "Venus", "Earth"};
Stream<String> allLetters2 =
Stream.of(planets).flatMap(word -> letters(word));
allLetters2.forEach(System.out::print);
//flatMap 執行一對多的轉換,然後將所有的Map都展開
//['M','e','r','c','u','r','y',
// 'V','e','n','u','s',
// 'E','a','r','t','h']
Stream<Stream<String>> allLetters =
Stream.of(planets).map(word -> letters(word));
allLetters.forEach(System.out::print);
//[['M','e','r','c','u','r','y'],
// ['V','e','n','u','s'],
// ['E','a','r','t','h']]
System.out.println("======對每個元素進行計算,最後綜合返回經過合併的Stream==");
}
public static Stream<String> letters(String word) {
List<String> result = new ArrayList<>();
for(int i=0;i<word.length();i++)
{
result.add(word.substring(i, i+1));
}
return result.stream();
}
}
6.limit抽取:
//獲取前n個元素
Stream<Integer> s1 = Stream.of(1,2,3,4,5,6,7,8,9,10);
Stream<Integer> s2 = s1.limit(3);
s2.forEach(System.out::println);
7.skip跳過:
Stream<Integer> s3 = Stream.of(1,2,3,4,5,6,7,8,9,10);
Stream<Integer> s4 = s3.skip(8);
s4.forEach(System.out::println);
8.concat連接:
Stream<String> s5 = Stream.concat(letters("hello"), letters("world"));
s5.forEach(System.out::println);
9.額外調試peek
import java.util.stream.Stream;
public class StreamOther {
public static void main(String[] args) {
Stream<Double> s1 = Stream.iterate(1.0, n -> n*2)
.peek(n -> System.out.println("number:" + n)).limit(5);
s1.forEach(System.out::println);
}
}
輸出:
number:1.0
1.0
number:2.0
2.0
number:4.0
4.0
number:8.0
8.0
number:16.0
16.0
四、Optional類型
Optional<T>介紹
- 一個包裝器對象
- 要麼保證類型T對象,要麼沒有包裝任何對象(還是NULL)
- 如果T有值,那麼直接返回T的對象
- 如果T是NULL,那麼可以返回一個替代物
Optional<T>創建:
- of方法
- empty方法
- ofNullable方法,對於對象可能偉null情況下安全創建
直接用get方法optional爲空則會引發NOSuchElementException異常
isPresent判斷非常低效一般不用。
五、流的計算結果
流的計算:
- 簡單約簡(聚合函數):count/max/min/...
- 自定義約簡:reduce
- 查看/遍歷元素:iterator/forEach
- 存放到數據結構當中
1.簡約約簡:
2.自定義約簡:
import java.util.Optional;
import java.util.stream.Stream;
public class Reduce {
public static void main(String[] args) {
Integer[] a = new Integer[] {2,4,6,8};
Stream<Integer> s1 = Stream.of(a);
Optional<Integer> sum = s1.reduce(Integer::sum);
System.out.println(sum.get());
Stream<Integer> s2 = Stream.of(a);
Optional<Integer> product = s2.reduce((x,y)->x*y);
System.out.println(product.get());
Stream<Integer> s3 = Stream.of(a);
Integer product3 = s3.reduce(1,(x,y)->x*y);//給定初始值
System.out.println(product3);
String[] b = new String[] {"abc","def","ghi"};
Stream<String> s4 = Stream.of(b);
String bigStr = s4.reduce("",(x,y)->x+y);
System.out.println(bigStr);
}
}
輸出:
20
384
384
abcdefghi
3.遍歷和查看:
import java.util.Iterator;
import java.util.stream.Stream;
public class StreamView {
public static void main(String[] args) {
Integer[] a = new Integer[] {2,4,6,8};
Stream<Integer> s1 = Stream.of(a);
Iterator<Integer> it1 = s1.filter(n->n>2).iterator();
while(it1.hasNext()) {
System.out.println(it1.next());
}
Stream<Integer> s2 = Stream.of(a);
s2.filter(n->n>2).forEach(System.out::println);
}
}
4.存放打數據結構中:
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class StreamCollect {
public static void main(String[] args) {
Integer[] a = new Integer[] {2,4,6,8};
//將流存儲爲List
Stream<Integer> s1 = Stream.of(a);
List<Integer> list1 = s1.collect(Collectors.toList());
//將流存儲爲指定的LinkedList
Stream<Integer> s2 = Stream.of(a);
List<Integer> list2 = s2.collect(Collectors.toCollection(LinkedList::new));
//將流存儲爲Set
Stream<Integer> s3 = Stream.of(a);
Set<Integer> set1 = s3.collect(Collectors.toSet());
//將流變換爲字符流,並連接起來
Stream<Integer> s4 = Stream.of(a);
String result = s4.map(String::valueOf).collect(Collectors.joining());
System.out.println(result); //2468
//將流變換爲字符流,並連接起來
Stream<Integer> s5 = Stream.of(a);
String result2 = s5.map(String::valueOf).collect(Collectors.joining(","));
System.out.println(result2); //2,4,6,8
List<Person> persons = new ArrayList<Person>();
persons.add(new Person(1, "Jerry"));
persons.add(new Person(2, "Tom"));
//將流存儲爲Map
Stream<Person> s6 = persons.stream();
Map<Integer, String> map1 = s6.collect(Collectors.toMap(Person::getId, Person::getName));
for(Integer i:map1.keySet())
{
System.out.println("id:" + i + ", name:" + map1.get(i));
}
}
}
class Person
{
int id;
String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person(int id, String name) {
super();
this.id = id;
this.name = name;
}
}
六、流的應用
注意事項:
- 一個流一次只能有一個用途
- 不要創建無線流
- 注意流的操作順序
- 謹慎使用並行流
並行流的使用前提:
參考中國大學mooc《java核心技術》