天天看點

Java8 新特性Java8新特性

Java8新特性

推薦閱讀:
  • CSDN-雲深不知處:Java8 Stream:2萬字20個執行個體,玩轉集合的篩選、歸約、分組、聚合
  • CSDN-高可用架構:你可能未曾使用的新 Java 特性
  • lambda表達式、方法引用、構造器引用、數組引用
  • Stream流
  • optional
  • 時間日期API [線程安全]
  • 重複注解與類型注解
  • ForkJoin架構
  • 接口的預設方法和靜态方法

1. Lambda表達式

Java8中引入了一個新的操作符 “->” 該操作符稱位箭頭操作符
  • 定義一個接口(函數式接口)
@FunctionalInterface
public interface MyFunction {

    Integer getValue(Integer num);
}
           
  • 使用lambda表達式
public class LambdaTest2 {

    public Integer operation(Integer num, MyFunction mf) {
        return mf.getValue(num);
    }

    @Test
    public void test1() {
        Integer num = operation(100, (x) -> x * x);
        System.out.println(num);

        Integer num2 = operation(100, (x) -> x + 200);
        System.out.println(num2);
    }

}
           

1.1 内置四大核心函數式接口

接口名稱 接口類型 方法 方法參數 方法傳回值類型
Consumer 消費型 void accept(T t) ×
Supplier 供給型 T get() ×
Function<T,R> 函數型 R apply (T t)
Predicate 斷言型 boolean test(T t)

消費型接口

/**
     * Consumer<T>: 消費型接口
     * 需求:
     */
    public void happy(double money, Consumer<Double> con) {
        con.accept(money);
    }

    @Test
    public void happyTest() {
        happy(10000, m -> System.out.println("今天吃飯花了" + m));
    }
           

供給型接口

/**
     * Supplier<T>:供給型接口
     * 需求:随機存儲10個數字存入集合
     */
    public List<Integer> getNumList(int num, Supplier<Integer> sup) {
        List<Integer> list = new ArrayList<>();

        for (int i = 0; i < num; i++) {
            Integer n = sup.get();
            list.add(n);
        }
        System.out.println(list);
        return list;
    }

    @Test
    public void supTest() {
        getNumList(10, () -> (int) (Math.random() * 100));
    }
           

函數型接口

/**
     * Function<T,R>: 函數型接口
     */
    public String strHandler(String str, Function<String, String> fun) {
        return fun.apply(str);
    }

  
    @Test
    public void funTest() {
        String s = strHandler("\t\t\t 你是誰啊! \t\t\t", (str) -> str.trim());
        System.out.println(s);
    }
           

斷言型接口

/**
     * Predicate<T>: 斷言型接口
     * 需求:将滿足條件的字元串放入集合
     */
    public static List<String> filterStr(List<String> strList, Predicate<String> pre) {
        List<String> list = new ArrayList<>();
        for (String str : strList) {
            if (pre.test(str)) {
                list.add(str);
            }
        }
        return list;
    }

    @Test
    public void preHandler(){
        List<String> stringList = Arrays.asList("Hello", "Friends", "Lambda", "www", "ok");
        List<String> strings = filterStr(stringList, (str) -> str.length() > 3);
        System.out.println(strings);
    }
           

1.2 方法引用

若Lambda體中的内容有方法已經實作了,我們可以使用“方法引用”(可了解為是lambda表達式的另外一種表現形式)

有三種文法形式

  • 對象::執行個體方法名
  • 類名::靜态方法名
  • 類名::執行個體方法名

注意

  1. Lambda體中調用方法的參數清單與傳回值類型,要與函數式接口中抽象方法的函數清單和傳回值類型保持一緻!
  2. 若Lambda參數清單中的第一參數是執行個體方法的調用者,而第二個參數是執行個體方法的參數時,可使用 ClassName::methodName

對象::執行個體方法名

/**
     * 對象::執行個體方法名
     */
    @Test
    public void test1() {
        PrintStream ps = System.out;
        // 方法引用
        Consumer<String> con = ps::println;
        con.accept("aqwsdasagas");
    }
           

類名::靜态方法名

/**
     * 類::靜态方法名
     */
    @Test
    public void test2() {
        // lambda
        Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
        // 方法引用
        Comparator<Integer> com1 = Integer::compare;
    }
           

類::執行個體方法名

/**
     * 類::執行個體方法名
     */
    @Test
    public void test3() {
        BiPredicate<String, String> bp = (x, y) -> x.equals(y);
        BiPredicate<String, String> bp1 = String::equals;
    }

 @Test
    public void test2() {
        Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
        Comparator<Integer> com1 = Integer::compare;
    }
           

1.3 構造器引用

注意:需要調用的構造函數的參數清單要與函數式接口中抽象方法的參數清單保持一緻!
前提定義Employee和枚舉類
  • Employee
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Employee {

    private int id;
    private String name;
    private int age;
    private double salary;
    private Status status;

    public Employee(int id) {
        this.id = id;
    }
}
           
  • 構造器引用案例
/**
     * 構造器引用: 獲得了一個無參構造
     */
    @Test
    public void test4() {
        // lambda
        Supplier<Employee> sup = () -> new Employee();
        // 構造器引用
        Supplier<Employee> sup2 = Employee::new;
        // 應該獲得了一個無參構造,因為get方法就是無參有傳回值
        Employee employee = sup2.get();
        System.out.println(employee);
    }

    /**
     * 構造器引用:擷取參數為int id的構造方法
     */
    @Test
    public void test5() {
        Function<Integer, Employee> fun = (x) -> new Employee(x);
        Employee apply = fun.apply(2);
        Function<Integer, Employee> fun2 = Employee::new;
        // 根據apply的參數個數去調用對應的構造方法(一個參數,int類型)
        Employee apply2 = fun.apply(3);
        System.out.println(apply);
        System.out.println(apply2);
    }
           

1.4 數組引用

Type::new
/**
     * 數組引用
     */
    @Test
    public void test7(){
        Function<Integer,String[]> fun = (x) -> new String[x];
        String[] strs = fun.apply(10);
        System.out.println(strs.length);

        Function<Integer,String[]> fun2 = String[]::new;
        System.out.println(fun2.apply(15).length);
    }
           

1.5 情景處理

某公司員工定制排序

package cn.luis.lambda.practise;

import cn.luis.lambda.method.Employee;
import org.junit.jupiter.api.Test;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class Compony {

    /**
     * 某公司員工基本資訊
     */
    List<Employee> emps = Arrays.asList(
            new Employee(1,"zhanagsan", 18, 1111.11),
            new Employee(2,"lisi", 49, 2222.22),
            new Employee(3,"zhaoliu", 49, 3333.33),
            new Employee(4,"tianqi", 35, 4444.44)
    );


    /**
     * 定制排序:先按年齡比,年齡相同按姓名比
     */
    @Test
    public void test1() {
        Collections.sort(emps, (e1, e2) -> {
            if (e1.getAge() == e2.getAge()) {
                return e1.getName().compareTo(e2.getName());
            } else {
                return Integer.compare(e1.getAge(),e2.getAge());
            }
        });

        System.out.println("======================");

        for (Employee emp : emps) {
            System.out.println(emp);
        }

    }

}

           

字元串處理

package cn.luis.lambda.practise;

import org.junit.jupiter.api.Test;

public class StringTest {
    /**
     * 函數式接口
     */
    interface StringFun {
        String getValue(String str);
    }

    /**
     * 用于處理字元串
     */
    public String strHandler(String str, StringFun sf) {
        return sf.getValue(str);
    }


    /**
     * 字元串處理
     */
    @Test
    public void test1() {
        String s1 = strHandler("\t\t\t hello , may i have a dog \t\t\t", str -> str.trim());
        System.out.println(s1);
        String s2 = strHandler("asdfghjkl", str -> str.toUpperCase());
        System.out.println(s2);
        String s3 = strHandler("asdfghjkl", str -> str.substring(0, 3));
        System.out.println(s3);
    }

}

           

long型數字處理

package cn.luis.lambda.practise;

import org.junit.jupiter.api.Test;

public class LongTest {

    /**
     * 函數式接口
     */
    public interface MyFunction<T, R> {

        R getValue(T t1, T t2);

    }

    /**
     * 對于兩個long型資料進行處理
     * MyFunction<Integer,Integer>: 傳入參數類型、傳回值類型
     */
    public void operacte(Integer x, Integer y, MyFunction<Integer, Integer> mf) {
        System.out.println(mf.getValue(x, y));
    }

    @Test
    public void test1() {
        operacte(100, 100, (x, y) -> x + y);
    }
}

           

2. Stream流

Stream的三個操作步驟

  • 建立Stream
  • 中間操作
  • 終止操作(終端操作)

2.1 建立Stream

建立stream的四種方式

  • 通過Collection系列集合提供的

    stream()

    方法或

    parallelStream()

List<String> list = new ArrayList<>();
        Stream<String> stream1 = list.stream();
		Stream<String> stream2 = list.parallelStream();
           
  • 通過靜态方法擷取一個數組流
Employee[] employees = new Employee[10];
        Stream<Employee> stream2 = Arrays.stream(employees);
           
  • 通過Stream類靜态類中的of方法
  • 建立無限流:兩種(疊代和生成)
// 4.1疊代 : 傳一個起始值和一個一進制運算
        Stream<Integer> stream4 = Stream.iterate(0, (x) -> x + 2);
        /// 中間操作  終止操作
        stream4.limit(10).forEach(System.out::println);

        // 4.2生成(無限生成随機數)
        Stream.generate(()->Math.random()).forEach(System.out::println);
           

2.2 中間操作

中間操作:不會執行任何操作,終止操作:一次性執行全部内容,即“惰性求值”

2.2.1 篩選與切片

方法 描述
filter 接收Lambda,從流中排除某些元素
limit 截斷流,使其元素不超過給定數量
skip(n) 跳過元素,傳回一個扔掉了前n個元素的流,若流中元素不足n個,則傳回一個空流,與limit(n)互補
distinct 篩選,通過流所生成元素的hashCode() 和 equals() 去除重複元素

例子:

  • filter
List<Employee> employees = Arrays.asList(
            new Employee(1, "張三", 18, 1111.11),
            new Employee(2, "李四", 49, 2222.22),
            new Employee(3, "趙六", 57, 3333.33),
            new Employee(4, "田七", 35, 4444.44),
            new Employee(4, "田七", 35, 4444.44)
    );	

    /**
     * 中間操作:不會執行任何操作,終止操作:一次性執行全部内容,即“惰性求值”
     * 1.篩選與切片:filter
     */
	@Test
    public void test1() {
        // 内部疊代:疊代操作由Stream API完成
        employees.stream()
                .filter(e -> e.getAge() > 35)
                .forEach(System.out::println);

        // 外部疊代
        Iterator<Employee> it = employees.iterator();

        while (it.hasNext()) {
            System.out.println(it.next());
        }
    }
           
  • limit
package cn.luis.stream.intermediate;

import cn.luis.stream.Employee;
import org.junit.jupiter.api.Test;

import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

public class LimitTest {

    List<Employee> employees = Arrays.asList(
            new Employee(1, "張三", 18, 1111.11),
            new Employee(3, "趙六", 57, 3333.33),
            new Employee(4, "田七", 35, 4444.44),
            new Employee(2, "李四", 49, 2222.22),
            new Employee(5, "田七", 35, 4444.44)
    );

    /**
     * 中間操作:不會執行任何操作,終止操作:一次性執行全部内容,即“惰性求值”
     * 2.篩選與切片:limit
     */
    @Test
    public void test2() {
        // limit;短路,找到兩個符合的就終止了
        employees.stream()
                .filter(e -> {
                    //System.out.println("不符合條件的或者直接抛棄了!" + e);
                    return e.getAge() > 20;
                })
                .limit(2)
                .forEach(System.out::println);
    }

}
           
  • skip
/**
     * 中間操作:不會執行任何操作,終止操作:一次性執行全部内容,即“惰性求值”
     * 1.篩選與切片:skip
     */
    @Test
    public void test3() {
        // limit;跳過前兩個
        employees.stream()
                .filter(e -> {
                    System.out.println("呦!");
                    return e.getAge() > 25;
                })
                .skip(2)
                .forEach(System.out::println);
    }
           
  • distinct
/**
     * 中間操作:不會執行任何操作,終止操作:一次性執行全部内容,即“惰性求值”
     * 1.篩選與切片:distinct
     */
    @Test
    public void test4() {
        // distinct;去重 【employees要重寫hashcode和equals】
        employees.stream()
                .filter(e -> {
                    System.out.println("呦!");
                    return e.getAge() > 25;
                })
                .distinct()
                .forEach(System.out::println);
    }
           

2.2.2 映射

map裡傳入Function函數型接口,傳入一個參數傳回一個值
方法 描述
map 接收lambda,将元素轉換成其他形式或提取資訊,接收一個函數作為參數,該函數會被應用到每個元素上,并将其映射成一個新的元素。
flatMap 接收一個函數作為參數,将該流中的每一個值都換成另一個流,然後把所有流連成一個流

例子:

public class StreamApi3 {

    List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd");

    @Test
    public void test1() {
        list.stream()
                .map(s -> s.toUpperCase())
                .forEach(System.out::println);

        System.out.println("--------------------------");


        // 這種套娃式可以用flatMap替代 (見test2)
        Stream<Stream<Character>> stream = list.stream().map(StreamApi3::filterCharacter);
        stream.forEach(sm->{
            sm.forEach(System.out::println);
        });
    }


    public static Stream<Character> filterCharacter(String str) {
        List<Character> list = new ArrayList<>();
        for (Character ch : str.toCharArray()) {
            list.add(ch);
        }
        return list.stream();
    }

    @Test
    public void test2() {
        // 扁平化maop:原來把流放入map流,現在是直接将流中的元素放入flatmap流中
        // 把{a,a,a},{b,b,b} ... 轉換成了 {a,a,a,b,b,b,c,c,c}
        Stream<Character> characterStream = list.stream()
            .flatMap(StreamApi3::filterCharacter);
        
        characterStream.forEach(System.out::println);
    }
}
           

2.2.3 排序

方法 描述
sorted() 自然排序(Comparable)
sorted(Comparator com) 定制排序(Comparator)

例子:

/**
     * 中間操作:不會執行任何操作,終止操作:一次性執行全部内容,即“惰性求值”
     * 3. 排序
     * sorted() -- 自然排序(Comparable)
     * sorted(Comparator com) -- 定制排序(Comparator)
     */
    @Test
    public void test1() {

        // 自然排序
        Stream<Employee> sorted = employees.stream().sorted();
        // 按年齡排序,年齡相同按名字排序
        employees.stream()
                .sorted((e1, e2) -> {
                    if (e1.getAge() == e2.getAge()) {
                        return e1.getName().compareTo(e2.getName());
                    } else {
                        Integer x = e1.getAge();
                        return x.compareTo(e2.getAge());
                    }
                }).forEach(System.out::println);
    }
           

2.3 終止操作

2.3.1 查找與比對

方法 描述
allMatch 檢查是否比對所有元素
anyMatch 檢查是否至少比對一個元素
noneMatch 檢查是否沒有比對所有元素
findFirst 傳回第一個元素
findAny 傳回目前流中的任意元素
count 傳回流中元素的總個數
max 傳回流中的最大值
min 傳回流中的最小值

例子:

  • 查找的對象
List<Employee> employees = Arrays.asList(
            new Employee(1, "張三", 18, 1111.11, Status.BUSY),
            new Employee(2, "李四", 49, 2222.22, Status.FREE),
            new Employee(3, "趙六", 57, 3333.33, Status.BUSY),
            new Employee(4, "田七", 35, 4444.44, Status.VOCATION),
            new Employee(2, "李十四", 49, 2222.22, Status.FREE),
            new Employee(2, "李十四", 49, 2222.22, Status.FREE),
            new Employee(2, "李十四", 49, 2222.22, Status.FREE)
    );
           
  • 查找與比對
@Test
    public void test1() {
        boolean b = employees.stream()
                .allMatch(e -> e.getStatus().equals(Status.BUSY));
        System.out.println(b);

        boolean b2 = employees.stream()
                .anyMatch(e -> e.getStatus().equals(Status.BUSY));
        System.out.println(b2);

        boolean b3 = employees.stream()
                .noneMatch(e -> e.getStatus().equals(Status.BUSY));
        System.out.println(b3);

        Optional<Employee> firste = employees.stream()
                .sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))
                .findFirst();
        System.out.println(firste.get());

        Optional<Employee> anye = employees.stream()
                .filter(e -> e.getStatus().equals(Status.FREE))
                .findAny();
        System.out.println(anye.get());

        // 并行
        Optional<Employee> anye2 = employees.parallelStream()
                .filter(e -> e.getStatus().equals(Status.FREE))
                .findAny();
        System.out.println(anye2.get());
    }
           
  • 查找與比對
@Test
    public void test2() {
        long count = employees.stream()
                .count();
        System.out.println(count);

        Optional<Employee> maxe = employees.stream()
                .max((e1, e2) -> Double.compare(e1.getAge(), e2.getAge()));
        System.out.println(maxe.get());

        Optional<Double> min = employees.stream()
                .map(Employee::getSalary)
                .min(Double::compare);
        System.out.println(min.get());
    }
           

2.3.2 規約

方法 描述
reduce 可以将流中的元素反複結合起來,得到一個值

例子:

/**
     * reduce(起始值,二進制運算): 規約
     */
    @Test
    public void test3() {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
        // 先把起始值0作為x,再從list取數放入y
        Integer sum = list.stream()
                .reduce(0, (x, y) -> x + y);
        System.out.println(sum);

        System.out.println("----------計算公司總工資---------");
        Double reduce = employees.stream()
                .map(e -> e.getSalary())
                .reduce(0.0, (x, y) -> x + y);
        // 優化寫法(map-reduce模式)
        Optional<Double> reduce1 = employees.stream()
                .map(Employee::getSalary)
                .reduce(Double::sum);
        System.out.println(reduce1.get());
    }
           

2.3.3 收集

包括集合、計算、分組、分區
方法 描述
collect 将流轉換為其他形式,接收一個Collectot接口的實作,用于給Stream中的元素彙總的方法
  • 集合
@Test
    public void test4() {
        List<String> nameList = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.toList());
        nameList.forEach(System.out::println);
        System.out.println("-----------過濾重複資料--------------");
        Set<String> nameSet = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.toSet());
        nameSet.forEach(System.out::println);
        System.out.println("-----------放到其他類型集合--------------");
        HashSet<String> nameHashSet = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.toCollection(HashSet::new));
        nameHashSet.forEach(System.out::println);
    }
           
  • 計算
/**
     * collect 之 計算
     */
    @Test
    public void test5() {
        // 總數
        Long collect = employees.stream()
                .collect(Collectors.counting());
        System.out.println(collect);

        // 平均值
        Double avg = employees.stream()
                .collect(Collectors.averagingDouble(Employee::getSalary));
        System.out.println(avg);

        // 總和
        Double sum = employees.stream()
                .collect(Collectors.summingDouble(Employee::getSalary));
        System.out.println(sum);

        // 最大值
        Optional<Employee> maxo = employees.stream()
                .collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
        System.out.println(maxo.get());

        // 最小值
        Optional<Double> mino = employees.stream()
                .map(Employee::getSalary)
                .collect(Collectors.minBy(Double::compare));
        System.out.println(mino.get());
    }
           
  • 分組
/**
     * collect 之 分組
     */
    @Test
    public void test6() {
        // 分組
        Map<Status, List<Employee>> statusListMap = employees.stream()
                .collect(Collectors.groupingBy(Employee::getStatus));
        /*statusListMap.keySet().forEach(System.out::println);
        statusListMap.entrySet().forEach(System.out::println);
        System.out.println(statusListMap);*/

        // 多級分組
        /*employees.stream()
                .collect(Collectors.groupingBy(Employee::getStatus), Collectors.groupingBy((Employee e) -> {
                    if (e.getAge() <= 35) {
                        return "青年";
                    }else if (e.getAge() > 20) {
                        return "老年";
                    } else {
                        return "少年";
                    }
                }));*/

        // 分區: 滿足條件的分一個區,不滿足的分一個區
        Map<Boolean, List<Employee>> collect = employees.stream()
                .collect(Collectors.partitioningBy(e -> e.getSalary() > 2000.0));
        System.out.println(collect);

        // Double:強大計算
        DoubleSummaryStatistics aDouble = employees.stream()
                .collect(Collectors.summarizingDouble(Employee::getSalary));
        System.out.println(aDouble.getAverage());
        System.out.println(aDouble.getMax());
        System.out.println(aDouble.getCount());

        // 字元串拼接
        String str = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.joining(","));
        System.out.println(str);
    }
           

2.4 應用

  • 交易員類
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Trander {

    private String name;
    private String city;

}
           
  • 交易類
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Transaction {

    private Trander trander;
    private int year;
    private int value;
}
           
  • 練習
package cn.luis.stream.practise;

import org.junit.Before;
import org.junit.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author Luis
 * @version 1.0
 * @description TODO
 * @date 2020/12/8 9:25
 */
public class TransactionTest {

    List<Transaction> transactions;

    @Before
    public void before() {
        Trander raoul = new Trander("Raoul", "Cambridge");
        Trander mario = new Trander("Mario", "Milan");
        Trander alan = new Trander("Raoul", "Cambridge");
        Trander brian = new Trander("Brian", "Cambridge");

        transactions = Arrays.asList(
                new Transaction(brian, 2011, 300),
                new Transaction(raoul, 2012, 1000),
                new Transaction(raoul, 2011, 400),
                new Transaction(mario, 2012, 710),
                new Transaction(mario, 2012, 700),
                new Transaction(alan, 2012, 950)
        );
    }

    /**
     * 找到2011年發生的所有交易,并按交易額排序(從低到高)
     * [有條件先篩選,排序記得寫規則]
     */
    @Test
    public void test1() {
        transactions.stream()
                .filter(t -> t.getYear() == 2011)
                .sorted((t1, t2) -> Integer.compare(t1.getValue(), t2.getValue()))
                .forEach(System.out::println);
    }

    /**
     * 交易員都在哪些不同的城市工作過
     * [記得去重]
     */
    @Test
    public void test2() {
        transactions.stream()
                .map(t -> t.getTrander().getCity())
                .distinct()
                .forEach(System.out::println);
    }

    /**
     * 查找所有來自劍橋的交易員,并按姓名排序
     */
    @Test
    public void test3() {
        transactions.stream()
                .map(t -> t.getTrander())
                .distinct()
                .filter(trander -> "Cambridge".equals(trander.getCity()))
                .sorted((t1, t2) -> t1.getName().compareTo(t2.getName()))
                .forEach(System.out::println);

        System.out.println("---------------------------------");

        transactions.stream()
                .map(t -> t.getTrander().getName())
                .sorted()
                .forEach(System.out::println);

        System.out.println("---------------------------------");

        String reduceStr = transactions.stream()
                .map(t -> t.getTrander().getName())
                .sorted()
                .reduce("", String::concat);
        System.out.println(reduceStr);

        System.out.println("---------------------------------");
    // 将所有姓名組成字元串,然後按照子母排排序
        transactions.stream()
                .map(t->t.getTrander().getName())
                .flatMap(TransactionTest::filterCharacter)
                .sorted()
                .forEach(System.out::print);
    }

    public static Stream<Character> filterCharacter(String str) {
        List<Character> list = new ArrayList<>();
        for (Character ch : str.toCharArray()) {
            list.add(ch);
        }
        return list.stream();
    }

    /**
     * 傳回所有交易員的姓名字元串,按字母順序排序
     */
    @Test
    public void test4() {
        String collect = transactions.stream()
                .map(Transaction::getTrander)
                .map(Trander::getName)
                .sorted(String::compareToIgnoreCase)
                .collect(Collectors.joining(","));
        System.out.println(collect);
    }

    /**
     * 有沒有交易員是在米蘭工作的
     */
    @Test
    public void test5() {
        boolean b = transactions.stream()
                .map(Transaction::getTrander)
                .map(Trander::getCity)
                .noneMatch(c -> "Milan".equals(c));

        System.out.println(!b);

    }

    /**
     * 列印生活在劍橋的交易員的所有交易額
     */
    @Test
    public void test6() {
        Optional<Integer> reduce = transactions.stream()
                .filter(t -> "Cambridge".equals(t.getTrander().getCity()))
                .map(Transaction::getValue)
                .reduce(Integer::sum);
        System.out.println(reduce.isPresent());
        System.out.println(reduce.get());
    }

    /**
     * 所有交易中,最高的交易額
     */
    @Test
    public void test7() {
        Optional<Integer> max = transactions.stream()
                .map(Transaction::getValue)
                .max(Double::compare);
        System.out.println(max.get());

        System.out.println("------------------------------------------");

        Optional<Integer> collect = transactions.stream()
                .map(Transaction::getValue)
                .collect(Collectors.maxBy((v1, v2) -> v1.compareTo(v2)));
        System.out.println(collect.get());
    }
}
           

3. DateAPI

3.1 人讀的時間

api 描述
LocalDate 日期 年月日
LocalTime 時間 時分秒
LocalDateTime 時間和日期 年月日+時分秒

例子:

@Test
    public void test1() {
        LocalDateTime ldt = LocalDateTime.now();
        System.out.println(ldt);

        LocalDateTime ldt2 = LocalDateTime.of(2020, 12, 9, 12, 56);
        System.out.println(ldt2);

        LocalDateTime ldt3 = ldt.plusYears(2);
        System.out.println(ldt3);

        LocalDateTime ldt4 = ldt.minusMonths(2);
        System.out.println(ldt4);

        System.out.println("----------------------------");

        int year = ldt.getYear();
        int monthValue = ldt.getMonthValue();
        int dayOfMonth = ldt.getDayOfMonth();
        int hour = ldt.getHour();
        int minute = ldt.getMinute();
        int second = ldt.getSecond();
        System.out.println(year);
        System.out.println(monthValue);
        System.out.println(dayOfMonth);
        System.out.println(hour);
        System.out.println(minute);
        System.out.println(second);

    }
           

結果:

2020-12-10T17:05:55.025
2020-12-09T12:56
2022-12-10T17:05:55.025
2020-10-10T17:05:55.025
----------------------------
2020
12
10
17
5
55
           

3.2 機器讀的時間

方法 描述
Instant 時間戳(以unix元年:1970年1月1日00:00:00到某個時間之間的毫秒值)
Duration 計算兩個“時間”之間的間隔
Period 計算兩個“日期”之間的間隔

例子:

@Test
    public void test2() {
        // 預設擷取UTC時區(格林威治)的時間
        Instant in1 = Instant.now();
        System.out.println(in1);

        // 加8小時
        OffsetDateTime odt = in1.atOffset(ZoneOffset.ofHours(8));
        System.out.println(odt);

        // 毫秒值
        long haomiao = in1.toEpochMilli();
        System.out.println(haomiao);

        Instant instant = Instant.ofEpochSecond(1000);
        
    }	
           
  • 計算時間間隔
/**
     * Duration:計算;兩個“時間”之間的間隔
     */
    @Test
    public void test3() throws InterruptedException {
        Instant in1 = Instant.now();
        Thread.sleep(3000);
        Instant in2 = Instant.now();
        Duration duration = Duration.between(in1, in2);
        // 擷取秒和納秒用get~(),擷取毫秒用to~()
        System.out.println(duration.toMillis()); // 3001

        LocalTime lt = LocalTime.now();
        Thread.sleep(2000);
        LocalTime lt2 = LocalTime.now();
        System.out.println(Duration.between(lt, lt2).toMillis()); // 2001
        System.out.println(Duration.between(lt2, lt).toMillis()); // -2001

    }
           
  • 計算日期間隔
/**
     * Period:計算;兩個“日期”之間的間隔
     */
    @Test
    public void test4() throws InterruptedException {
        LocalDate ld1 = LocalDate.now();
        LocalDate ld2 = LocalDate.of(2015, 1, 1);
        Period period = Period.between(ld2, ld1);
        System.out.println(period);
        System.out.println(period.getYears());
        System.out.println(period.getMonths());
        System.out.println(period.getDays());
        
        /**
            P5Y11M9D
            5
            11
            9
        */

    }
           

3.3 時間校正器

api 描述
TemporalAdjuster 時間校正器
TemporalAdjusters 時間校正器工具類

例子:

@Test
    public void test5() {
        LocalDateTime ldt = LocalDateTime.now();
        System.out.println(ldt);
        // 将月中的天指定為10
        LocalDateTime ldt2 = ldt.withDayOfMonth(10);
        System.out.println(ldt2);

        // 下一個周日
        LocalDateTime ldt3 = ldt.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
        System.out.println(ldt3);

        // 自定義:下一個工作日是什麼時候
        LocalDateTime gzr = ldt.with(l -> {
            LocalDateTime ldt4 = (LocalDateTime) l;
            DayOfWeek dayOfWeek = ldt4.getDayOfWeek();
            if (dayOfWeek.equals(DayOfWeek.FRIDAY)) {
                return ldt4.plusDays(4);
            } else if (dayOfWeek.equals(DayOfWeek.SATURDAY)) {
                return ldt4.plusDays(2);
            } else {
                return ldt4.plusDays(1);
            }
        });
        System.out.println(gzr);
        System.out.println("工作日: " + gzr.getMonthValue() + "月" + gzr.getDayOfMonth() + "日");
    }
           

3.4 格式化時間、日期

api

api 描述
DateTimeFormatter 時間格式化

方法

api 描述 舉例
.ofPattern() 定義日期轉換格式 DateTimeFormatter.ofPattern(“yyyy-MM-dd HH:mm:ss”);

例子:

@Test
    public void test6() {

        LocalDateTime ldt = LocalDateTime.now();
        System.out.println(ldt);

        // DateTimeFormatter、LocalDateTime 都有format方法
        DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        // String strDate1 = dtf2.format(ldt);
        String strDate2 = ldt.format(dtf2);
        System.out.println(strDate2);
        LocalDateTime ldt2 = LocalDateTime.parse(strDate2, dtf2);
        System.out.println(ldt2);

    }
           

3.5 時區的處理

api 描述
ZoneId 時區
ZonedDate 時間
ZonedTime 日期
ZonedDateTime 日期和時間

例子:

/**
     * 時區的處理:ZonedDate、ZonedTime、ZonedDateTime
     */
    @Test
    public void test7() {
        Set<String> zoneIds = ZoneId.getAvailableZoneIds();
        //zoneIds.forEach(System.out::println);
        //System.out.println(zoneIds.stream().count()); // 599個時區

        // 指定時區
        LocalDateTime now1 = LocalDateTime.now();
        // 兩種指定時區方法
        LocalDateTime now2 = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
        // 兩種指定時區方法
        ZonedDateTime zdt = now1.atZone(ZoneId.of("Asia/Shanghai"));
        System.out.println(now1);
        System.out.println(now2);
        System.out.println(zdt);
    }
           

3.6 Instant、LocalDateTime、Date間的轉換

3.6.1 Date to LocalDateTime

/**
     * Date to LocalDateTime
     */
    @Test
    public void test9() {
        Date todayDate = new Date();
        System.out.println("todayDate = " + todayDate);

        LocalDateTime todayLocalDateTime = todayDate.toInstant()
                .atZone(ZoneId.systemDefault())
                .toLocalDateTime();

        System.out.println("todayLocalDateTime = " + todayLocalDateTime);
    }
           

結果:

todayDate = Fri Dec 11 10:26:00 CST 2020
todayLocalDateTime = 2020-12-11T10:26:00.115
           

3.6.2 LocalDateTime to Date

/**
     * LocalDateTime to Date
     */
    @Test
    public void test10() {
        LocalDateTime localDateTime = LocalDateTime.now();
        System.out.println("localDateTime = " + localDateTime);

        Date date = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());

        System.out.println("date = " + date);
    }
           

結果:

localDateTime = 2020-12-11T10:29:17.403
date = Fri Dec 11 10:29:17 CST 2020
           

3.6.3 綜合

通過:ZonedDateTime
/**
     * Instant、LocalDateTime、Date轉換
     */
    @Test
    public void test8() {
        ZoneId zoneId = ZoneId.of("Asia/Shanghai");

        Instant instant = Instant.now();
        ZonedDateTime zonedDateTime = instant.atZone(zoneId);

        System.out.println("instant=== " + instant);
        System.out.println("zonedDateTime=== " + zonedDateTime);

        LocalDateTime localDateTime = zonedDateTime.toLocalDateTime();
        System.out.println("localDateTime=== " + localDateTime);

        Date date = Date.from(instant);
        System.out.println("date=== " + date);
    }
           

結果:

instant=== 2020-12-11T02:21:52.939Z
zonedDateTime=== 2020-12-11T10:21:52.939+08:00[Asia/Shanghai]
localDateTime=== 2020-12-11T10:21:52.939
date=== Fri Dec 11 10:21:52 CST 2020
           

3.6.4 DateUtil

import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
 
public class Dateutili {
 
    public static Date asDate(LocalDate localDate) {
        return Date.from(localDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
    }
 
    public static Date asDate(LocalDateTime localDateTime) {
        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    }
 
    public static LocalDate asLocalDate(Date date) {
        return Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDate();
    }
 
    public static LocalDateTime asLocalDateTime(Date date) {
        return Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDateTime();
    }
}
           

4. Optional

4.1 API

api Optional
位置 java.util.Optional
描述 是一個容器類,是一個可以為null的容器對象

4.2 方法

4.2.1 建立Optional類對象的方法

方法 描述
Optional.of(T t) 建立一個Optional執行個體
Optional.empty() 建立一個空的Optional執行個體
Optional.ofNullable(T t) 若t不為null,建立Optional執行個體,否則建立空執行個體

例子:

Optional.of(T t) 建立一個Optional執行個體
/**
     * Optional.of(T t) 建立一個Optional執行個體
     */
    @Test
    public void test1() {
        Girl girl = new Girl();
        girl = null;
        // of(T t): 保證t是非空的
        Optional<Girl> girl1 = Optional.of(girl);
        System.out.println(girl1);
    }
           
Optional.ofNullable(T t) 若t不為null,建立Optional執行個體,否則建立空執行個體
@Test
    public void test2() {
        Girl girl = new Girl();
        girl = null;
        // ofNullable(T t): t可以為null
        Optional<Girl> girl1 = Optional.ofNullable(girl);
        System.out.println(girl1);
    }
           

4.2.2 判斷Optional容器中是否包含對象

方法 描述
isPresent() 判斷是否包含對象
void idPresent(Consumer<? super ?> consumer) 如果有值,就執行Consumer接口的實作代碼,并且該值會作為參數傳給它

4.2.3 擷取Optional容器的對象

方法 描述
T get() 如果調用對象包含值,傳回該值,否則抛異常
orElse(T other) 如果調用對象包含值,傳回該值,否則傳回 other對象
orElseGet(Supplier<? extends ?> s) 果調用對象包含值,傳回該值,否則傳回 s 擷取的值
T orElseThrow(Supplier<? extends X> exceptionSupplier) 如果有值将其傳回,否則抛出由Supplier接口實作提供的異常
map(Function f) 如果有值對其處理,并傳回處理後的Optional,否則傳回Optional.empty()
flatMap(Function mapper) 與map類似,要求傳回值必須是Optional

4.3 情景應用

定義一個boy類、girl類
  • boy
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Boy {

    private Girl girl;
}
           
  • girl
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Girl {

    private String name;
}
           
測試類
如果boy為null,則空指針會在本類抛出,若name為空,則在getGirlName方法抛出

原始寫法

package cn.luis.lambda.atguigu.optional;

import org.junit.Test;

import java.util.Optional;

public class OptionalTest {

    public String getGirlName(Boy boy) {
        return boy.getGirl().getName();
    }

    /**
     * 練習
     * 如果boy為null,則空指針會在本類抛出,若name為空,則在getGirlName方法抛出
     */
    @Test
    public void test3() {
        Boy boy = new Boy();
        //boy = null;
        String girlName = getGirlName(boy);
        System.out.println(girlName);
    }
}
           

普通優化

package cn.luis.lambda.atguigu.optional;

import org.junit.Test;

import java.util.Optional;

public class OptionalTest {

    /**
     * 普通優化
     */
    public String getGirlName1(Boy boy) {
        if (boy != null) {
            Girl girl = boy.getGirl();
            if (girl != null) {
                return boy.getGirl().getName();

            }
        }
        return null;
    }

    /**
     * 普通優化
     * 如果boy為null,則空指針會在本類抛出,若name為空,則在getGirlName方法抛出
     */
    @Test
    public void test33() {
        Boy boy = new Boy();
        //boy = null;
        String girlName = getGirlName1(boy);
        System.out.println(girlName);
    }
}
           

使用Optional優化

package cn.luis.lambda.atguigu.optional;

import org.junit.Test;

import java.util.Optional;

public class OptionalTest {

    /**
     * Optional優化
     */
    public String getGirlName2(Boy boy) {
        Girl girl = boy.getGirl();
        Optional<Girl> girlOptional = Optional.ofNullable(girl);
        Girl girl1 = girlOptional.orElse(new Girl("Mary"));
        System.out.println(girl1.getName());
        return null;
    }

    /**
     * Optional優化
     */
    @Test
    public void test333() {
        //Boy boy = new Boy();
        Boy boy = new Boy(new Girl("LiLy"));
        Optional<Boy> boyOptional = Optional.ofNullable(boy);
        Boy boy1 = boyOptional.orElse(new Boy());
        String girlName = getGirlName1(boy1);
        System.out.println(girlName);
    }
}

           

5. 可重複注解、類型注解

5.1 可重複注解

重複注解:1. 定義注解,2.定義注解容器,3在注解上添加@Repeatable(容器注解.class)
  • 自定義注解
@Repeatable(MyAnns.class)
@Target({TYPE, METHOD,FIELD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnn {

    String value() default "luis";
}
           
  • 定義一個注解容器
@Target({TYPE, METHOD,FIELD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnns {

    MyAnn[] value();
}
           
  • 測試類
public class AnnotationTest {

     /**
     * 重複注解:1. 定義注解,2.定義注解容器,3在注解上添加@Repeatable(容器注解.class)
     */
    @MyAnn("hello")
    @MyAnn("world")
    public void show() {
    }

    /**
     * 利用反射擷取注解的值
     */
    @Test
    public void test1(){
        Class<AnnotationTest> clazz = AnnotationTest.class;
        try {
            Method showMethod = clazz.getMethod("show");

            MyAnn[] ann = showMethod.getAnnotationsByType(MyAnn.class);
            for (MyAnn myAnn : ann) {
                System.out.println(myAnn.value());
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
}
           

5.2 類型注解

java8需要搭配架構或者自己定義注解才可
public class AnnotationTest {

    /**
     * 類型注解,java8需要搭配架構或者自己定義注解才可
     * @Target({ElementType.TYPE, METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.CONSTRUCTOR, ElementType.LOCAL_VARIABLE})
     */
    // 提供編譯時檢查,不允許為null
    public /*@NonNull*/ Object obj = null;
}
           

6. ForkJoin架構

[了解]:對并發優化!
package cn.luis.lambda.atguigu.fork_join;

import java.util.concurrent.RecursiveTask;

/**
 * @author Luis
 * @version 1.0
 * @description TODO  RecursiveAction:無傳回值
 * @date 2020/12/9 9:51
 */
public class FoekJoinCalculate extends RecursiveTask<Long> {

    private static final long serialVersionUID = 134656970987L;

    private long start;
    private long end;
    // 臨界值
    private static final long THRESHOLD = 10000;

    public FoekJoinCalculate(long start, long end) {
        this.start = start;
        this.end = end;
    }

    @Override
    protected Long compute() {
        long length = end - start;

        if (length <= THRESHOLD) {
            long sum = 0;

            for (long i = start; i <= end; i++) {
                sum += 1;
            }
            return sum;
        } else {
            long middle = (start + end) / 2;
            FoekJoinCalculate left = new FoekJoinCalculate(start, middle);
            // 拆分子任務,同時壓入線程隊列
            left.fork();

            FoekJoinCalculate right = new FoekJoinCalculate(middle + 1, end);
            // 拆分子任務,同時壓入線程隊列
            right.fork();

            return left.join()+right.join();
        }
    }
}

           
  • 測試類
package cn.luis.lambda.atguigu.fork_join;

import org.junit.Test;

import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream;

/**
 * @author Luis
 * @version 1.0
 * @description TODO
 * @date 2020/12/9 10:20
 */
public class ForkTest {

    /**
     * ForkJoin架構 (和普通for對比,要資料特大才有效果,因為拆分也需要時間)
     */
    @Test
    public void test1() {
        // 計算時間戳
        Instant start = Instant.now();

        ForkJoinPool pool = new ForkJoinPool();
        ForkJoinTask<Long> task = new FoekJoinCalculate(0, 10000000000L); //1876
        Long sum = pool.invoke(task);

        System.out.println(sum);

        Instant end = Instant.now();

        System.out.println("耗費時間:" + Duration.between(start, end).toMillis()); //34
    }

    /**
     * 普通for
     */
    @Test
    public void test2() {
        // 計算時間戳
        Instant start = Instant.now();
        long sum = 0L;
        for (long i = 0; i <= 10000000000L; i++) {
            sum += i;
        }
        System.out.println(sum);
        Instant end = Instant.now();

        System.out.println("耗費時間:" + Duration.between(start, end).toMillis()); //4324
    }

    /**
     * java8并行流
     */
    @Test
    public void test3(){
        // 生成long型随機數

        // 串行流
        long reduce = LongStream.rangeClosed(0, 10000000000L)
                .reduce(0, Long::sum);
        System.out.println(reduce);

        System.out.println("------------------------------");

        // 串行流
        long reduce2 = LongStream.rangeClosed(0, 10000000000L)
                .parallel()
                .reduce(0, Long::sum);
        System.out.println(reduce2);


    }

}
           
本文總結自B站-尚矽谷

END

繼續閱讀