工作後, 你一定不能錯過技術之JDK1.8的新特性

在現在的企業級開發中, 隨着新技術的迭代, 越來越多的公司開始使用Java8的新特性去簡化開發, 因此Java8非常值得我們學習.
在學習Java8的時候, 我們需要了解一下Java8都有哪些主要的新特性

  • 函數式接口
  • Lambda表達式
  • Stream集合的流式編程
  • 新時間日期API等

而在企業開發中, 主要用到的便是Lambda表達式Stream流 , 而在下面,我們便主要的去學習這兩方面的知識

Lambda表達式

Lambda表達式本質上是一個匿名函數, 可以簡單理解爲一個可以將代碼像數據一樣傳遞的代碼
特點: 簡潔, 靈活

基本語法

在寫lambda表達式時, 只需關注函數和方法體即可

  • 參數: 寫在小括號中
  • 方法體: 寫在大括號內
  • ->: lambda表達式運算符, 分隔參數和方法體

注意:

  • lambda主要用於簡化函數式接口, 而函數式接口在Java中是指: 有且僅有一個抽象方法的接口
  • 只有確保接口中有且僅有一個抽象方法,Java中的Lambda才能順利地進行推導

舉例:

定義了三個接口, 利用lambda去實現相關功能

/**
 * Author TimePause
 * Create  2020-05-03 22:03
 */
public interface SingleReturnMultipleParameter {
    int test(int a,int b);
}

/**
 * Author TimePause
 * Create  2020-05-03 22:04
 */
public interface NoneReturnParmarter {
    void test();
}

/**
 * Author TimePause
 * Create  2020-05-03 22:26
 */
public interface SingleReturnSingleParameter {
    int test(int x);
}


/**
 * lambda表達式: 對接口進行簡潔的實現
 *
 * @author TimePause
 * @create 2020-05-03 22:05
 */
public class TestLambda {
    public static void main(String[] args) {
        //測試lambda無返回值無參
        NoneReturnParmarter lambda1= ()-> {
            System.out.println("lambda-NoneReturnParmarter");
        };
        lambda1.test();
        //測試lambda單個返回值多個參數
        SingleReturnMultipleParameter lambda2=(int a,int b)->{
            return a + b;
        };
        System.out.println(lambda2.test(1, 2));
        //測試lambda單個參數單個返回值
        SingleReturnSingleParameter lambda3=(int x)->{
            return x*x;
        };
        System.out.println(lambda3.test(3));

        //測試lambda單個/多個參數省略參數類型
        SingleReturnMultipleParameter lambda5=(a,b)->{
            return a + b;
        };
        System.out.println(lambda5.test(1, 2));

        //測試lambda單個參數省略小括號(兩個無法省略)
        SingleReturnSingleParameter lambda6= x->{return x*x; };

        /**
         *  注意:
         *  1.如果方法體中代碼只有一行,那麼大括號可以省略
         *  2.如果唯一的一行代碼是返回語句,那麼在大括號省略的同時, 返回值也必須省略
         */
        NoneReturnParmarter lambda7= ()-> System.out.println("lambda-NoneReturnParmarter");
        SingleReturnSingleParameter lambda8= x-> x*x;

    }
}

lambda表達式的函數引用

  • 將一個接口的實現,引用到一個已存在的方法上
  • 注意: 要引用的方法的參數和返回值要和接口中定義的一致

舉例:

使用上面實現過的接口來實現引用

/**
 * Author TimePause
 * Create  2020-05-03 22:03
 */
public interface SingleReturnMultipleParameter {
    int test(int a,int b);
}

/**
 * 測試lambda表達式的引用
 *
 * @author TimePause
 * @create 2020-05-04 22:11
 */
public class TestLambda2 {
    public static void main(String[] args) {
        //測試lambda表達式的引用: 類名::方法名
        SingleReturnMultipleParameter lambda=TestLambda2::getAbs;
        lambda.test(2, 3);
        //相當於下面的方式
        SingleReturnMultipleParameter lambda1 = (a, b) -> TestLambda2.getAbs(a, b);
        System.out.println(lambda1.test(2, 3));
        //測試對非靜態方法的引用
        NoneReturnParmarter lambda2=new TestLambda2()::show;
        lambda2.test();
    }

    /**
     * 引用注意:
     * 1.把私有修飾符去掉以後可在其他類中通過 類名::方法名引用其他方法
     * 2.如果是靜態方法引用方式爲: 類名::方法名
     *   如果是非靜態方法引用方式爲: 對象::方法名
     */
     static int getAbs(int a,int b){
        return a>b?a-b:b-a;
    }

    public void show(){
        System.out.println("如果是非靜態方法引用方式爲: 對象::方法名");
    }
}

lambda表達式對構造方法的引用

lambda在引用時,會根據接口類型不同自動適配是帶參還是無參的構造方法!!!

/**
 * 測試lambda表達式對構造方法的引用
 *
 * @author TimePause
 * @create 2020-05-04 22:38
 */
public class TestLambda3 {
    public static void main(String[] args) {
        //lambda在引用時,會根據接口類型不同自動適配是帶參還是無參的構造方法!!!
        PersonMake lambda1=Person::new;
        PersonMake2 lambda2 = Person::new;
    }
}
//定義一個實體類兩個接口, 分別測試lambda對帶參和無參方法的引用
class Person{
    String name;
    int age;

    Person(){}
    Person(String name,int age){
        this.name = name;
        this.age = age;
    }
}

interface PersonMake{
    Person getPerson();
}

interface  PersonMake2{
    Person getPerson(String name,int age);
}

lambda表達式對get(),set()方法的引用

引用時需要根據接口進行適配

public class TestLambda3 {
    public static void main(String[] args) {
        //對get方法的引用
        getName lambda4=Person::getName;
        //對set方法的引用
        setName lambda5=new Person()::setName;
    }
}
class Person{
    String name;
    int age;
    
    public String getName() {
        return name;
    }
    public int getAge() {
        return age;
    }
    public void setName(String name) {
        this.name = name;
    }
    public void setAge(int age) {
        this.age = age;
    }
}


interface  getName{
    String get(Person p);
}

interface setName{
    void set(String name);
}

lambda簡單應用1

/**
 * 測試lambda表達式實現數組的降序排序
 *
 * @author TimePause
 * @create 2020-05-05 9:24
 */
public class TestLambda4 {
    public static void main(String[] args) {
        Integer arr[] = {1, 9, 8, 3, 5, 6};
        //ctrl+f12=>查看當前類下所有方法
        Arrays.sort(arr);//這是正序
        System.out.println(Arrays.toString(arr));
        Arrays.sort(arr,(x,y)-> y-x);//這是倒序
        System.out.println(Arrays.toString(arr));
    }
}

簡單應用2

將上面的TestLambda3 代碼進行修改,可以測試lambda對對象數組的某個屬性進行排序的情況

public class TestLambda3 {
    public static void main(String[] args) {
        Person[] persons=new Person[5];
        persons[0] = new Person("小1", 33);
        persons[1] = new Person("小2", 21);
        persons[2] = new Person("小3", 25);
        persons[3] = new Person("小4", 28);
        persons[4] = new Person("小5", 19);
        //利用lambda對其按照年齡進行正序排序
        Arrays.sort(persons,(p1,p2)->p1.getAge()-p2.getAge());
        for (int i = 0; i < persons.length; i++) {
            System.out.println(persons[i]);
        }
    }
}
//定義一個實體類兩個接口, 分別測試lambda對帶參和無參方法的引用
class Person{
    String name;
    int age;

    Person(){}
    Person(String name,int age){
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

Stream(集合的流式編程)

在對集合中的元素進行操作的時候, 有時候可能會用到其他結果, 在這個過程中使用集合的流式編程可以簡化代碼量,
將數據源中的數據讀取到一個流中, 可以對這個流進行操作(刪除, 過濾, 映射…)

介紹

Stream流是對集合操作的增強, 它不是一種數據結構,也不負責存儲. 更像一個迭代器,且單向遍歷不可循環

實現步驟

  1. 獲取數據源, 讀取到流中
  2. 對流中的數據進行操作(中間操作)
  3. 對流中的數據進行整合處理(最終操作)

注意:

  • 幾乎所有中間操作最終操作的參數和方法都是函數式接口,
  • 因此使用集合的流式編程進行簡化處理的前提是熟練使用lambda表達式

數據源的獲取

數據源就是數據的來源, 從數據源中讀取到流中
需要注意的是對流中數據的操作(刪除, 映射, 過濾…)是不會影響數據源的數據的

/**
 * 測試Stream流式編程獲取數據源
 *
 * @author TimePause
 * @create 2020-05-05 11:34
 */
public class TestStream {
    public static void main(String[] args) {
        List<Object> list = new ArrayList<>();
        /**
         * 數據源的獲取
         * 1.stream獲取的數據源是串行的
         * 2.parallelStream獲取的數據源是並行的, 且該方法封裝了多線程對數據的操作, 效率更高
         */
        list.stream();
        list.parallelStream();

        //Stream獲取數組元素
        int[] arr={1,9,8,0,2,3};
        IntStream stream = Arrays.stream(arr);
    }
}

最終操作

最終操作指的是將流中的數據整合在一起, 放入一個集合, 也可以直接對流中的數據進行遍歷統計, 提取出我們想要的信息
注意: 在使用最終操作後會關閉這個流並銷燬流中的數據, 如果再次使用這個已關閉的流則會出現異常

/**
 * 測試最終操作
 *
 * @author TimePause
 * @create 2020-05-05 12:09
 */
public class TestStream2 {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<Integer>();
        Collections.addAll(list, 5, 10, 15, 20, 25);
        /**
         * 1.collect:
         * 通過在collect()方法內調用Collectors的其他方法,將流中的數據轉化成其他類型的數據
         */
        //將最終結果轉成list
        List<Integer> collect = list.stream().collect(Collectors.toList());
        System.out.println(collect);
        //將最終結果轉成set
        Set<Integer> set = list.stream().collect(Collectors.toSet());
        System.out.println(set);
        //將最終結果轉成map
        Map<Integer, Integer> map = list.stream().collect(Collectors.toMap(e -> e / 5, e -> e));
        System.out.println(map);

        /**
         * 2.reduce:將流中的所有元素帶入這個方法中進行運算
         * 最終的運算結果是一個optional類型的數據,需要調用get()方法獲取其中的數據
         */
        int res = list.stream().reduce((e1, e2) -> e1 + e2).get();
        System.out.println(res);

        /**
         * 3.count:統計流中的數據的個數
         */
        long count = list.stream().count();
        System.out.println(count);

        /**
         * 4.foreach:遍歷流中的元素
         */
        list.stream().forEach(System.out::println);

        /**
         * 5.max&min: 獲取流中的最大值和最小值
         */
        Integer max = list.stream().max(Integer::compareTo).get();
        System.out.println("集合中的最大值是: "+max);
        Integer min = list.stream().min(Integer::compareTo).get();
        System.out.println("集合中的最小值是: "+min);

        /**
         * 6.allMatch & anyMatch & noneMatch=>matching
         * allMatch: 只有流中所有元素都滿足匹配規則,才返回true
         * anyMatch: 只要有任意一個元素滿足匹配規則,就返回true
         * noneMatch: 只有流中所有的元素都不滿足匹配規則,才返回true
         */
        boolean allMatch = list.stream().allMatch(e -> e > 20);
        System.out.println(allMatch);
        boolean anyMatch = list.stream().anyMatch(e -> e > 20);
        System.out.println(anyMatch);
        boolean noneMatch = list.stream().noneMatch(e -> e %5 !=0);
        System.out.println(noneMatch);

        /**
         * 7.find: 查找流中的第一個元素
         * findFirst()和findAny()在絕大多數情況下作用是一樣的, 只有在多線程的環境下才可能不同
         */
        Integer findFirst = list.parallelStream().findFirst().get();
        Integer findAny = list.parallelStream().findAny().get();
        System.out.println(findFirst +"---"+findAny);

		/**
         * 注意事項:在使用最終操作後會關閉這個流並銷燬流中的數據, 如果再次使用這個已關閉的流則會出現異常
         * 異常打印如下:
         * Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
         * 	at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:229)
         * 	at java.util.stream.ReferencePipeline.findFirst(ReferencePipeline.java:464)
         */
        Stream<Integer> stream = list.stream();
        long count1 = stream.count();
        System.out.println(count1);
        Integer integer = stream.findFirst().get();
        System.out.println(integer);
    }
}

中間操作

中間操作指的是對流中的數據做各式各樣的處理, 中間操作可以是連續的操作, 每次操作都返回一個Stream對象, 直到最終操作的執行

/**
 * 測試Stream的中間操作(對數組,集合進行操作)
 *
 * @author TimePause
 * @create 2020-05-05 15:45
 */
public class TestStream3 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list, "hello", "world", "hello", "TimePause", "csdn");
        /**
         * 1.filter:過濾流中符合條件的元素
         */
        list.stream().filter(e->e.length()>5).forEach(System.out::println);

        /**
         * 2.distinct: 去除集合中重複的元素
         * 這個方法沒有參數, 去重規則與hashset相同(會比較hashcode,如果hashcode相同會比較equals,equals相同則認爲相同則去重)
         */
        list.stream().distinct().forEach(System.out::println);

        /**
         * 3.sorted: 排序
         * sorted():默認是按照流中的元素對應的類,實現的comparable接口中的方法進行排序
         *          也可以在()內將流中的數據按照指定的規則進行排序
         */
        list.stream().sorted().forEach(System.out::println);
        list.stream().sorted((e1,e2)->e1.length()-e2.length()).forEach(System.out::println);

        /**
         * 4.skip & limit (這兩個方法一般會聯合使用)
         * skip(): 跳過指定個數的元素
         * limit():截取指定個數的元素
         */
        list.stream().skip(2).forEach(System.out::println);
        list.stream().limit(2).forEach(System.out::println);
        //如何獲取 hello TimePause?
        list.stream().skip(2).limit(2).forEach(System.out::println);

        /**
         * 5. map & flatMap (重要!!!)
         * map: 對流中的數據進行映射,用新的數據替換舊的數據
         * flatMap: 也是元素的映射,不過是扁平化的映射, 將容器中所有元素取出放到集合中
         */
        list.stream().map(e->e+".txt").forEach(System.out::println);

        String[] strs = {"hello world", "hello csdn", "hello TimePause", "hello csdn"};
        //數組也可以使用Stream
        Arrays.stream(strs).map(String::toCharArray).forEach(e-> System.out.println(Arrays.toString(e)));
        Arrays.stream(strs).map(e->e.split(" ")).flatMap(Arrays::stream).forEach(System.out::println); //strs->map(arrays)->flatMap(elements)

    }
}

/**
 * 測試Stream中間操作之maptoint (對對象組成的集合進行操作)
 *
 * @author TimePause
 * @create 2020-05-05 20:58
 */
public class TestStream4 {
    public static void main(String[] args) {
        ArrayList<Persons> person = new ArrayList<>();
        Collections.addAll(person,new Persons("小a",14),new Persons("小b",12),new Persons("小c",33));

        //Integer sum = person.stream().map(e -> e.getAge()).reduce((e1, e2) -> e1 + e2).get();
        //System.out.println(sum);
        /**
         * maptoInt: 將流中的數據轉成int,此時這個方法的返回值不再是Stream
         *           此時這個方法的返回值不再是Stream而是IntStream
         */
        double average = person.stream().mapToInt(Persons::getAge).average().getAsDouble();//獲取平均年齡
        System.out.println(average);
    }
}
class Persons{
    String name;
    int age;

    public Persons(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public int getAge() {
        return age;
    }
}

綜合案例

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 綜合練習
 *
 * @author TimePause
 * @create 2020-05-05 21:27
 */
public class TestStream5 {
    public static void main(String[] args) {
        ArrayList<Student> studentList = new ArrayList<>();
        Collections.addAll(studentList,
                new Student("小1",59),
                new Student("小2",99),
                new Student("小3",87),
                new Student("小4",68),
                new Student("小5",71)
        );

        //1.求出所有及格的學生
        System.out.println("---------------1.求出所有及格的學生--------------");
        studentList.
                stream().
                filter(e->e.getScore()>60).
                forEach(System.out::println);

        //2.求出所有及格的學生的姓名
        System.out.println("---------------2.求出所有及格的學生的姓名--------------");
        studentList.
                stream().
                filter(e->e.getScore()>60).
                map(Student::getName).
                forEach(System.out::println);

        //3.求出所有學生的平均成績
        System.out.println("---------------3.求出所有學生的平均成績--------------");
        double averageScore = studentList.
                stream().
                mapToInt(Student::getScore).
                average().getAsDouble();
        System.out.println(averageScore);

        //4.求班級前三名(做成集合)???
        System.out.println("---------------4.求班級前三名(做成集合)--------------");
        List<Student> collect = studentList.
                stream().
                sorted((e1, e2) -> e2.getScore() - e1.getScore()).
                limit(3).
                collect(Collectors.toList());
        collect.forEach(System.out::println);

        //5.求班級的3-10名(做成集合)
        System.out.println("---------------5.求班級的3-10名(做成集合)--------------");
        List<Student> collect2 = studentList.
                stream().
                sorted((e1, e2) -> e2.getScore() - e1.getScore()).
                limit(10).
                skip(2).
                collect(Collectors.toList());
        collect2.forEach(System.out::println);

        //6.求所有不及格學生的平均成績
        System.out.println("---------------6.求所有不及格學生的平均成績--------------");
        double averageScore2 = studentList.
                stream().
                filter(e -> e.getScore() < 60).
                mapToInt(e -> e.getScore()).
                average().
                getAsDouble();
        System.out.println(averageScore2);

        //7.將及格的學生, 按照成績降序輸出所有信息
        System.out.println("---------------7.將及格的學生, 按照成績降序輸出所有信息--------------");
        studentList.
                stream().
                filter(e->e.getScore()>60).
                sorted((e1,e2)->e2.getScore()-e1.getScore()).
                forEach(System.out::println);


        //8.班級學生的總分=>從這裏可以看出很多時候e->e.getScore()可以替換Student::getScore,都是對get()方法的引用
        System.out.println("---------------8.班級學生的總分--------------");
        int res = studentList.stream().mapToInt(Student::getScore).reduce((e1, e2) -> e1 + e2).getAsInt();
        int res2 = studentList.stream().mapToInt(e->e.getScore()).reduce((e1, e2) -> e1 + e2).getAsInt();
        int res3 = studentList.stream().mapToInt(e -> e.getScore()).sum();
        System.out.println(res);
        System.out.println(res2);
        System.out.println(res3);
    }
}

class Student{
    private String name;
    private int score;

    public Student(String name,int score ){
        this.name=name;
        this.score = score;
    }

    public String getName(){
        return name;
    }

    public int getScore(){
        return score;
    }

    @Override
    public String toString() {
        return String.format("姓名: %s, 成績: %d", name, score);
    }
}

聰明的人能夠看到本文配套學習視頻哦: https://www.bilibili.com/video/BV1N7411v796?p=1

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