stream流(三)

在這裏插入圖片描述

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的其他方法

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-0ttun9yM-1591886055889)(C:\Users\wzj\AppData\Roaming\Typora\typora-user-images\1591885941437.png)]

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-rYUTX3UM-1591886055890)(C:\Users\wzj\AppData\Roaming\Typora\typora-user-images\1591885895381.png)]

  • 演示一下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=米蕉)]
         * }
         */
    }
}    

注:肯定不止這些,我列出來的都是比較常見的!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章