Stream流的操作
1.什麼是Stream
它是一個數據渠道,用於操作數據源(集合,數組等)所生成的元素序列,集合講的是數據,Stream講的是計算!使用Stream API對集合數據進行操作,就類似使用SQL執行的數據庫查詢!
2.爲什麼要使用Stream API
實際開發中,項目中多數數據源來自Mysql,Oracel關係型數據庫,但現在
還有來自Nosql的數據:如MongoDB,Redis,而這些NoSQL的數據就需要
java層面去處理
3.Stream的操作三個步驟
1.創建Stream:一個數據源(集合,數組),獲取Stream
2.中間操作:一箇中間操作鏈,對數據源進行一系列處理
3.終止操作:一旦執行終止操作,就執行中間操作,併產生結果,之後不會再
一:Stream流的實例化
四種方式:
a:通過集合對象.stream()b:通過Arrays類的stream()
c:stream本身的of()
d:stream本身的iterate()、generate()
package cn.itcast.stream;
import cn.itcast.lambda.Employee;
import cn.itcast.lambda.EmployeeData;
import org.junit.Test;
import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;
import java.util.stream.Stream;
/**
* 1.
* Stream關注的是數據的運算,和cpu打交道
* 集合關注的是數據的存儲,和內存打交道!
*
* 2.
* a.Stream自己不會存儲元素
* b.Stream不會改變源對象,相反他們會返回一個持有結果的新Stream
* c.Stream操作是延遲執行的,這意味着他們會等需要結果的時候纔去執行(懶加載)
*
* 3.Stream執行流程
* a.Stream的實例化
* b.一系列的中間過程(過濾,映射...)
* c.終止操作!
*
* 4.說明
* a.一箇中間操作鏈,對數據源的數據進行處理
* b.一旦執行終止操作,就執行中間操作鏈,併產生結果,之後不會再被使用!
*/
public class StreamTest {
//創建 Stream方式一: 通過集合
@Test
public void test1(){
List<Employee> employees = EmployeeData.getEmployees();
// default Stream<E> stream():返回一個順序流
Stream<Employee> stream = employees.stream();
//default Stream<E> parallelStream(): 返回一個並行流
Stream<Employee> employeeStream = employees.parallelStream();
}
//創建 Stream方式二: 通過數組
@Test
public void test2(){
int[] arr = {1,3,2,4};
//調用Arrays類的public static <T> Stream<T> stream(T[] array):返回一個流!
IntStream intStream = Arrays.stream(arr);
Employee employee1 = new Employee(1001,"wzj",22,50000);
Employee employee2 = new Employee(1002,"tom",22,50000);
Employee[] employees = new Employee[]{employee1,employee2};
Stream<Employee> stream = Arrays.stream(employees);
}
//創建 Stream方式三: 通過stream本身的of()
@Test
public void test3(){
Stream<String> stringStream = Stream.of("1", "2", "3");
}
//創建 Stream方式四: 創建無限流
/**
* UnaryOperator也是一個函數式接口!,所以可以用lambda表達式的方式!
*/
@Test
public void test4(){
//迭代
//public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
//遍歷前10給個偶數
Stream.iterate(0,t -> t+2).limit(10).forEach(System.out::println);
//生成
//public static<T> Stream<T> generate(Supplier<T> s)
Stream.generate(Math::random).limit(10).forEach(System.out::println);
}
}
二:Stream流的中間操作
三大分類
a:篩選與切片:filter()、limit()、skip()、distinct()b:映射:map()、flatMap()
c:排序:sorted()、sorted(Comparator c)
- 準備數據!!!
@Data
public class Employee {
private int id;
private String name;
private int age;
private double salary;
public Employee() {
}
public Employee(int id) {
this.id = id;
}
public Employee(int id, String name) {
this.id = id;
this.name = name;
}
public Employee(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public Employee(int id, String name, int age, double salary) {
this.id = id;
this.name = name;
this.age = age;
this.salary = salary;
}
}
public class EmployeeData {
public static List<Employee> getEmployees(){
List<Employee> list = new ArrayList();
list.add(new Employee(1001,"wzj",22,50000));
list.add(new Employee(1002,"tom",22,40000));
list.add(new Employee(1003,"zhangsan",24,30000));
list.add(new Employee(1004,"lisi",25,20000));
list.add(new Employee(1005,"wangwu",26,10000));
list.add(new Employee(1005,"wangwu",26,10000));
return list;
}
}
2.1:篩選與切片
public class StreamAPITest1 {
//1.篩選與切片
@Test
public void test1(){
List<Employee> employees = EmployeeData.getEmployees();
//1.filter(Predicate p) 接收lambda表達式,從流中排除某些元素
//查詢員工表中工資>10000的所有員工!
employees.stream().filter(e -> e.getSalary()>10000).forEach(System.out::println); //注意stream流關閉之後,必須重新開一個去使用!
System.out.println();
//2.limit(n) 截斷流,使其元素不超過給定數量
//查詢員工表中前三條記錄!
employees.stream().limit(3).forEach(System.out::println);
System.out.println();
//3.skip(n) 跳過元素,返回一個扔掉了前n個元素的流,若流中的元素不足n個,則返回一個空流
//跳過前三條記錄開始查詢
employees.stream().skip(3).forEach(System.out::println);
//4.distinct 篩選,通過流所生成元素的hashcode和equals去除重複元素!
employees.stream().distinct().forEach(System.out::println);
}
}
2.2:映射
public class StreamAPITest2 {
//映射
@Test
public void test1(){
//map(Function f) -接收一個函數作爲參數,將元素轉換成其他形式或提取信息,該函數會應用到每個函數上,並將其映射成一個新的元素!
List<String> list = Arrays.asList("aa", "bb", "cc");
list.stream().map(str -> str.toUpperCase()).forEach(System.out::println);
System.out.println();
//練習一:獲取員工姓名長度>3的員工
List<Employee> employees = EmployeeData.getEmployees();
Stream<String> nameStream = employees.stream().map(Employee::getName);
nameStream.filter(name -> name.length()>3).forEach(System.out::println);
System.out.println();
//練習二: 這裏返回的效果,相當於add(Object obj)的效果,[1, 2, 3, [4, 5, 6]] 見test2()
Stream<Stream<Character>> streamStream = list.stream().map(StreamAPITest2::fromStringToStream);
streamStream.forEach( s -> {
s.forEach(System.out::println);
});
System.out.println();
// flatMap(Function f) 接收一個函數作爲參數, 將流中的每個值轉換成另一個流, 然後把所有流連接成一個流.
Stream<Character> characterStream = list.stream().flatMap(StreamAPITest2::fromStringToStream);
characterStream.forEach(System.out::println);
}
//將字符串中的多個字符構成的集合轉換爲對應的Stream的實例!
public static Stream<Character> fromStringToStream(String str){
List<Character> list = new ArrayList<>();
for (char c : str.toCharArray()){
list.add(c);
}
return list.stream(); //alt+enter: 快速生成編譯時類型和返回值類型是什麼!
}
@Test
public void test2(){
List list1 = new ArrayList(Arrays.asList("1","2","3"));
List list2 = new ArrayList(Arrays.asList("4","5","6"));
// list1.add(list2);
// System.out.println(list1); //[1, 2, 3, [4, 5, 6]]: 這就好比map()的思想!
list1.addAll(list2);
System.out.println(list1); // [1, 2, 3, 4, 5, 6] :這就好比flatMap()的思想
}
}
2.3:排序
public class StreamAPITest3 {
/**
* 排序:
* 1.自然排序 sorted()
* 2.定製排序 sorted(Comparator com)
*/
@Test
public void test1(){
//自然排序 sorted()
List<String> list = new ArrayList<>(Arrays.asList("1","3","2","4"));
list.stream().sorted().forEach(System.out::println);
System.out.println();
//定製排序 sorted(Comparator com)
// List<Employee> employees = EmployeeData.getEmployees();
// employees.stream().sorted().forEach(System.out::println);
//cn.itcast.lambda.Employee cannot be cast to java.lang.Comparable: 因爲Employee沒有實現Comparator接口!
List<Employee> employees = EmployeeData.getEmployees();
employees.stream().sorted((o1,o2) ->{
int age = Integer.compare(o1.getAge(),o2.getAge());
if(age != 0){ //!=0,就說明年齡不相等!
return age;
}else{ //年齡相等,就按薪水排序!
return Double.compare(o1.getSalary(),o2.getSalary());
}
}).forEach(System.out::println);
}
}
三:Stream流的終止操作
3.1:匹配與查找
//小技巧:如果你想不起來該函數式接口的抽象方法,除了點進來自己看,還可以先自己用匿名生成出來的基礎去改!
public class StreamStopTest1 {
//匹配與查找
@Test
public void test(){
/**
* 1.allMatch(Predicate p) 檢查是否匹配所有元素!
* 練習:是否所有的員工的年齡都>18
*/
List<Employee> employees = EmployeeData.getEmployees();
System.out.println(employees.stream().allMatch(e -> e.getAge() > 18));
/**
* 2.anyMatch(Predicate p) 檢查是否至少匹配一個元素
* 練習:是否存在員工的工資>10000
*/
System.out.println(employees.stream().anyMatch(e -> e.getSalary() > 10000));
/**
* 3.noneMatch(Predicate p) 檢查是否沒有匹配的元素
* 練習:是否存在員工姓“王”
*/
System.out.println(employees.stream().noneMatch(e -> e.getName().startsWith("王")));
}
@Test
public void test2(){
// 3.findFirt() 返回第一個元素(注:可以排完序後返回第一個!)
List<Employee> employees = EmployeeData.getEmployees();
Optional<Employee> first = employees.stream().findFirst();
System.out.println(first);
// 4.findAny() 返回當前流中任意元素: 使用並行流,而不是串行流!
Optional<Employee> any = employees.parallelStream().findAny();
System.out.println(any);
}
@Test
public void test3(){
//5.count() 返回流中元素的個數
List<Employee> employees = EmployeeData.getEmployees();
long count = employees.stream().count();
System.out.println(count);
/**
* 6.max(Comparator c) 返回流中的最大值!
* 練習:返回最高的工資
*
* employees.stream().map(e -> e.getSalary()) 這個e就表示從流中拿出來一個一個的元素!
* map(e -> e.getSalary()) 映射嗎,左邊是員工(key), 右邊是員工的工資(value)
*/
Optional<Double> maxSalary = employees.stream().map(e -> e.getSalary()).max(Double::compareTo);
System.out.println(maxSalary); //Optional[50000.0]
/**
* 7.min(Comparator c) 返回流中的最小值!
* 練習:返回最低的工資
*/
Optional<Employee> minSalaryEmp = employees.stream().min((o1, o2) -> Double.compare(o1.getSalary(), o2.getSalary()));
System.out.println(minSalaryEmp); //Optional[Employee(id=1005, name=wangwu, age=26, salary=10000.0)] 因爲沒做map等映射,所以返回是整條記錄!
}
@Test
public void test4(){
//8.foreach(Consumer c) 內部迭代
List<Employee> employees = EmployeeData.getEmployees();
employees.stream().forEach(System.out::println);
System.out.println();
//9.使用集合的遍歷操作,外部迭代
employees.forEach(System.out::println);
}
}
3.2:歸約
@Test
public void test1(){
/**
* reduce(T identity, BinaryOperator): 可以將流中的元素反覆結合起來得到一個值,返回T
* identity: 是初始化的值!
* BinaryOperator接口繼承了BiFunction中的抽象方法:R apply(T t, U u);
*
* 練習:計算1-10自然數的和!
*/
List<Integer> nums = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Integer reduce = nums.stream().reduce(0, new BinaryOperator<Integer>() {
@Override
public Integer apply(Integer num1, Integer num2) {
return num1+num2;
}
});
System.out.println(reduce); //55
//lambda表達式
Integer reduce1 = nums.stream().reduce(0, (num1, num2) -> num1 + num2);
System.out.println(reduce1); //55
//方法引用: Integer apply(Integer integer, Integer integer2) 結構類似於 int sum(int a, int b)
Integer reduce2 = nums.stream().reduce(10, Integer::sum);
System.out.println(reduce2); //65, 因爲初始化是10
}
/**
* reduce(BinaryOperator): 可以將流中的元素反覆結合起來得到一個值,返回Optional<T>
*
* 練習:計算員工工資的總和!
*/
@Test
public void test2(){
List<Employee> employees = EmployeeData.getEmployees();
Stream<Double> salaryStream = employees.stream().map(e -> e.getSalary());
Optional<Double> reduce = salaryStream.reduce(Double::sum);
System.out.println(reduce);
}
3.3:收集
/**
* 收集
*/
@Test
public void test1(){
// collect(Collector c) 將流轉換成其他形式,接收一個Collector接口的實現!
// 練習:查找工資大於6000的員工,結果返回一個list或者set
List<Employee> employees = EmployeeData.getEmployees();
List<Employee> list = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toList());
System.out.println(list);
System.out.println();
//返回一個set集合
Set<Employee> set = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toSet());
System.out.println(set);
}
Collectors的其他方法
- 演示一下Collectors中的groupingBy方法
@Data
public class FruitBasket {//水果籃
private List<Fruit> fruits;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Fruit {//水果
private String kindId; //種類id
private String name; //水果
}
public class Demo {
public static void main(String[] args) {
//1.
FruitBasket fruitBasket = new FruitBasket();
List<Fruit> fruits = new ArrayList<Fruit>();
fruits.add(new Fruit("1","砂糖橘"));
fruits.add(new Fruit("1","蜜桔"));
fruits.add(new Fruit("2","無籽西瓜"));
fruits.add(new Fruit("2","麒麟西瓜"));
fruits.add(new Fruit("3","米蕉"));
fruitBasket.setFruits(fruits);
Map<String, List<Fruit>> collect = fruitBasket.getFruits().stream().collect(Collectors.groupingBy(fruit -> fruit.getKindId()));
System.out.println(collect);
/**
* {
* 1=[Fruit(kindId=1, name=砂糖橘), Fruit(kindId=1, name=蜜桔)],
* 2=[Fruit(kindId=2, name=無籽西瓜), Fruit(kindId=2, name=麒麟西瓜)],
* 3=[Fruit(kindId=3, name=米蕉)]
* }
*/
}
}
注:肯定不止這些,我列出來的都是比較常見的!