Java-Se-Java8

[TOC]html

Lambda 表達式(重點)

爲何使用 Lambda

Lambda是一個匿名函數,咱們能夠將 lambda 表達式理解爲一段能夠傳遞的代碼(將代碼像數據同樣傳遞)java

//原來的匿名內部類
    @Test
    public void test1(){
        Comparator<Integer> comparator = new Comparator<Integer>() {
            @Override
            public int compare(Integer x, Integer y) {
                return Integer.compare(x,y);
            }
        };
        TreeSet<Integer> ts = new TreeSet<>(comparator);
    }

    //使用 Lambda 表達式
    public void test2(){
        Comparator<Integer> comparator = (x, y) -> Integer.compare(x, y);
        TreeSet<Integer> ts = new TreeSet<>(comparator);
    }

PS:java8-foreachgit

ArrayList<Object> list = new ArrayList<>();
        list.forEach(System.out::println);

基礎語法

Lambda 表達式由三部分組成:github

  • 形參列表:0個或多個
  • 箭頭:->
  • 代碼塊:一條語句,或多條語句(用花括號)

示例:編程

public static void main(String[] args) {
        //無參、無返回值
        Runnable r1 = () -> System.out.println("");
        r1.run();

        //有一個參數、無返回值
        Consumer<String> consumer = (x) -> System.out.println(x);
        consumer.accept("這是傳給x的參數");

        //若只有一個參數,括號能夠不寫
        Consumer<String> consume2 = x -> System.out.println(x);
        consumer.accept("一個參數");

        //兩個以上參數,有返回值,Lambda 體中有多條語句
        Comparator<Integer> comparator = (x,y) -> {
            System.out.println("多參、返回值,Lambda體多條語句");
            return Integer.compare(x,y);
        };
        comparator.compare(1, 2);
    }

疑問:接口只能有一個抽象方法?數組

答:並非全部的接口可使用Lambda表達式,只有函數式接口才能使用Lambda表達式,即,有@FunctionalInterface修飾的接口才能使用Lambda,原則上接口只有一個未實現的方法。 ——20190904安全

參數列表類型能夠不寫,由於JVM能夠經過上下文推斷,判斷數據類型網絡

應用示例

1)調用Collections.sort()方法,經過定製排序比較兩個Employee(先按年齡,年齡相同再按姓名),使用Lambda做爲參數傳遞。多線程

public class MyTest {
    List<Employee> list = Arrays.asList(
            new Employee(101, "張三", 19, 3333.33),
            new Employee(102, "李四", 39, 5555.77),
            new Employee(103, "王五", 29, 9999.88),
            new Employee(104, "李二狗", 49, 3333.33),
            new Employee(105, "秦二春", 59, 9999.99)
    );

    @Test
    public void test1(){
        //使用Lambda
        Collections.sort(list,(e1,e2) -> {
            if (e1.getAge() == e2.getAge()) {
                return e1.getName().compareTo(e2.getName());
            }else {
                return Integer.compare(e1.getAge(),e2.getAge());
            }
        });

        //使用匿名內部類
        Collections.sort(list, new Comparator<Employee>() {
            @Override
            public int compare(Employee o1, Employee o2) {
                if (o1.getAge() == o2.getAge()) {
                    return o1.getName().compareTo(o2.getName());
                }else {
                    return Integer.compare(o1.getAge(),o2.getAge());
                }
            }
        });
        
        for (Employee e : list) {
            System.out.println(e);
        }
    }
}

四大內置函數式接口

函數式接口 參數類型 返回類型 方法 用途
Consumer<T>,消費型接口 T void void accept(T t); 對類型爲T的對象應用操做
Supplier<T>,供給型接口 T T get(); 返回類型爲T的對象
Function<T, R>,函數型接口 T R R apply(T t); 對類型爲T的對象應用操做,並返回R類型的對象
Predicate<T>,判定型接口 T boolean boolean test(T t); 肯定類型爲T的對象是否知足某約束

示例:app

public class FunctionInterfaceTest {
    /**
     * Consumer<T>,消費型示例
     */
    @Test
    public void test1(){
        happy(10000,(x) -> System.out.println("花費了:"+x+"元"));
    }

    public void happy(double money, Consumer consumer) {
        consumer.accept(money);
    }

    /**
     * Supplier<T>,供給型示例
     */
    @Test
    public void test2(){
        List<Integer> numList = getNumList(10, () -> (int)(Math.random() * 100));
        for (Integer i : numList) {
            System.out.println(i);
        }
    }

    public List<Integer> getNumList(int num, Supplier<Integer> supplier) {
        ArrayList<Integer> list = new ArrayList<>();
        for (int i = 0; i < num; i++) {
            Integer n = supplier.get();
            list.add(n);
        }
        return list;
    }

    /**
     * Function<T, R>,函數型接口
     */
    @Test
    public void test3(){
        String newStr = strHandler("ABC", (str) -> str.trim());
        System.out.println(newStr);
    }

    //處理字符串
    public String strHandler(String str, Function<String,String> fun){
        return fun.apply(str);
    }

    /**
     * Predicate<T>,判定型接口
     */
    @Test
    public void test(){
        List<String> list = Arrays.asList("abc", "www.baidu.com", "www.oschina.com", "ok");
        List<String> stringList = filterStr(list, (s -> s.length() > 3));//字符長度大於3
        for (String str : stringList) {
            System.out.println(str);
        }
    }

    public List<String> filterStr(List<String> list, Predicate<String> pre){
        ArrayList<String> strList = new ArrayList<>();
        for (String str : list) {
            if (pre.test(str)) {//判斷
                strList.add(str);
            }
        }
        return strList;
    }
}

總結:核心思想是面向接口編程,能夠無論具體實現,用到時再寫實現。不會寫的時候,就用匿名內部類代替,而後再轉爲lambda。——20190905

方法引用、構造器引用

語法格式:

種類 示例 對應的Lambda
引用類方法 類名::類方法 (a,b,..) -> 類名.類方法(a,b,...)
引用特定對象的實例方法 對象::實例方法 (a,b,..) -> 對象.實例方法(a,b,...)
引用某類對象的實例方法 類名::實例方法 (a,b,..) -> a.實例方法(a,b,...)
引用構造器 類名::new (a,b,..) -> new 類名(a,b,...)

引用與Lambda之間能夠轉換!

總結:當 Lambda 只有一行語句時,能夠用::的寫法,連入參都省了,前提是二者的形參列表、返回值相同。

有現成的方法,就引用;沒有,就本身用 Lambda 表達式寫。

1)引用類方法

@FunctionalInterface
interface Converter{
	Integer convert(String from);
}

Converter converter1 = from -> Integer.valueOf(from);

Converter converter1 = Integer::valueOf;

2)引用特定對象的實例方法

Converter converter2 = from -> "fkit.org".indexOf(from);

Converter converter2 = "fkit.org"::indexOf;

Integer value = converter2.convert("it");

3)引用某類對象的實例方法

@FunctionalInterface
interface MyTest
{
	String test(String a , int b , int c);
}

MyTest mt = (a , b , c) -> a.substring(b , c);//第一個參數做爲調用者,後面的參數所有傳給該方法做爲參數。

MyTest mt = String::substring;

String str = mt.test("Java I Love you" , 2 , 9);

4)引用構造器

@FunctionalInterface
interface YourTest
{
	JFrame win(String title);
}

YourTest yt = (String a) -> new JFrame(a);

YourTest yt = JFrame::new;

JFrame jf = yt.win("個人窗口");
System.out.println(jf);

總結:如何知道調用的是哪一個構造函數?構造函數入參跟接口中抽象方法的入參對應。

5)數組引用

@Test
public void test2(){
    Function<Integer, String[]> fun = (x) -> new String[x];
    String[] arr = fun.apply(10);
    System.out.println(arr.length);

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

接口中的默認方法、靜態方法

接口中能夠定義哪些?

[修飾符] interface 接口名 extends 父接口1,父接口2...
{
    零個到多個常量定義...
    零個到多個抽象方法定義...
    零個到多個內部類、接口、枚舉定義...
    零個到多個默認方法或靜態方法(類方法)定義...
}

爲何要有默認方法?

在 java 8 以前,接口與其實現類之間的 耦合度 過高了(tightly coupled),當須要爲一個接口添加方法時,全部的實現類都必須隨之修改。默認方法解決了這個問題,它能夠爲接口添加新的方法,而不會破壞已有的接口的實現。這在 lambda 表達式做爲 java 8 語言的重要特性而出現之際,爲升級舊接口且保持向後兼容(backward compatibility)提供了途徑。

參考:https://www.cnblogs.com/sidesky/p/9287710.html

接口默認方法的「類優先」原則:若一個接口中定義了一個默認方法,而另外一個父類或接口中又定義了一個同名的方法時

  • 選擇父類中的方法,若是一個父類提供了具體的實現,那麼接口中具備相同名稱和參數的默認方法會被忽略
  • 接口衝突,若是父接口提供一個默認方法,而另外一個接口也提供一個相同名稱和參數列表的方法(不論是否是默認方法),那麼必須覆蓋該方法來解決衝突

總結:

  • 接口中定義的變量,若是省略了修改符,則默認是 public static final
  • 接口中定義的普通方法,不論是否使用public abstract修飾,接口中的普通方法老是使用public abstract來修飾
  • 接口中普通方法不能有實現體,但類方法、默認方法必需要有實現體

示例:

public interface Output
{
	// 接口裏定義的成員變量只能是常量
	int MAX_CACHE_LINE = 50;
	// 接口裏定義的普通方法只能是public的抽象方法
	void out();
	void getData(String msg);
	// 在接口中定義默認方法,須要使用default修飾
	default void print(String... msgs)
	{
		for (String msg : msgs)
		{
			System.out.println(msg);
		}
	}
	// 在接口中定義默認方法,須要使用default修飾
	default void test()
	{
		System.out.println("默認的test()方法");
	}
	// 在接口中定義類方法,須要使用static修飾
	static String staticTest()
	{
		return "接口裏的類方法";
	}
}

如何調用接口中的默認方法、類方法?靜態方法直接接口名調用,默認方法用InterfaceA.super.foo();或實現類實例調用。

新日期、時間 API

新的API所在包:

java.time //日期、時間
java.time.chrono //年代,日本年號、民國、佛教
java.time.format //格式化
java.time.temporal //日期、時間運算
java.time.zone //時區

新的API解決了線程安全問題。

傳統日期格式化方法

原來的SimpleDateFormat存在線程安全問題:

@Test
    public void test1(){
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        Callable<Date> task = new Callable<Date>() {
            @Override
            public Date call() throws Exception {
                return sdf.parse("20190906");
            }
        };

        ExecutorService pool = Executors.newFixedThreadPool(1000);

        ArrayList<Future<Date>> result = new ArrayList<>();

        for (int i = 0; i < 10; i++) {
            result.add(pool.submit(task));
        }

        for (Future<Date> future : result) {
            try {
                System.out.println(future.get());
            } catch (Exception e) {
            } 
        }
    }

可使用 ThreadLocal 來控制線程安全:

public class DateFormatThradLocal {
    private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() { //此處是重點
        @Override
        protected DateFormat initialValue() {
            return new SimpleDateFormat("yyyy-MM-dd");
        }
    };
    
    public static Date convertToDate(String source) throws ParseException {
        return df.get().parse(source);
    }
    
    public static String convertToString(Date date) throws ParseException {
        return df.get().format(date);
    }
}

新的日期格式化方法:

/**
     * 使用java8的日期格式化API
     */
    @Test
    public void test3(){
        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        System.out.println(LocalDate.parse("2019-09-06",df));
    }

API使用示例

// LocalDate
    // LocalTime 
    // LocalDateTime 它們使用方式類似
    @Test
    public void test1(){
        LocalDateTime dt = LocalDateTime.now();//返回當前時期時間:2019-09-06T22:41:40.890
        System.out.println(dt);

        System.out.println(dt.plusDays(2));//加上兩天
        System.out.println(dt.minusDays(2));//減兩天,其它類推

        System.out.println(dt.getYear());
        System.out.println(dt.getMonthValue());
        System.out.println(dt.getDayOfMonth());//依次類推...
    }

    // Instant,時間戳(以 Unix 元年,即1970年1月1日零時到某個時間之間的毫秒值)
    @Test
    public void test2(){
        Instant ins = Instant.now();//默認是 UTC 時區
        System.out.println(ins.atOffset(ZoneOffset.ofHours(8)));// UTC + 8 時區(北京時間)
    }

    // Duration,計算兩個「時間」之間的間隔
    // Period,計算兩個「日期」之間的間隔
    @Test
    public void test3(){
        LocalDateTime ldt1 = LocalDateTime.now();
        //Thread.sleep(1000);//須要try-catch
        LocalDateTime ldt2 = LocalDateTime.now();
        Duration duration = Duration.between(ldt1, ldt2);
        System.out.println(duration);//默認是秒
        System.out.println(duration.toMinutes());//轉換成分鐘

        LocalDate ld1 = LocalDate.of(2015, 12, 9);
        LocalDate ld2 = LocalDate.now();
        Period period = Period.between(ld1, ld2);
        System.out.println(period);
        System.out.println(period.toTotalMonths());
    }
    
    // TemporalAdjuster,時間校訂器
    // TemporalAdjusters,工具類
    @Test
    public void test4(){
        LocalDateTime ldt = LocalDateTime.now();
        System.out.println(ldt);
        System.out.println(ldt.withDayOfMonth(20));//2019-09-20T10:37:44.230
    }

    // DateTimeFormatter,格式化時間、日期
    @Test
    public void test5(){
        LocalDateTime ldt = LocalDateTime.now();
        DateTimeFormatter df1 = DateTimeFormatter.ISO_DATE;//使用API提供的格式
        DateTimeFormatter df2 = DateTimeFormatter.ofPattern("yyyy年MM月dd日");//自定義格式

        System.out.println(ldt.format(df1));
        System.out.println(ldt.format(df2));
    }
    
    // ZonedDateTime
    @Test
    public void test6(){
        Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();// 獲取全部的時區ID
        availableZoneIds.forEach(System.out::println);

        ZoneId shanghai = ZoneId.of("Asia/Shanghai");
        System.out.println(shanghai.toString());

        LocalDateTime ldt = LocalDateTime.now(ZoneId.of("Europe/Monaco"));//指定時區建立時間
        System.out.println(ldt);
    }

Stream API(重點)

Stream(流) 是 Java8 中處理集合的關鍵抽象概念。Stream API 提供了一種高效且易於使用的處理數據的方式。

「集合講的是數據,流講的是計算!」。

注意:

  • Stream 本身不會存儲數據。
  • Stream 不會改變源對象。相反,他們會返回一個持有結果的新 Stream;
  • Stream 操做是延遲的。這意味着他們會等到須要結果時才執行;

操做步驟:

建立流

示例:

// 建立 Stream
    @Test
    public void test1(){
        // 方式一:經過 Collection 系列集合提供的 stream() 或 parallelstream()
        ArrayList<String> list = new ArrayList<>();
        Stream<String> stream1 = list.stream();

        // 方式二:經過 Arrays 中的靜態方法 stream
        Employee[] empArr = new Employee[10];
        Stream<Employee> stream2 = Arrays.stream(empArr);

        // 方式三:經過 Stream 類中的靜態方法 of()
        Stream<String> stream3 = Stream.of("ab", "cd", "ef");

        // 方式四:建立無限流
        Stream<Integer> stream4 = Stream.iterate(0, (x) -> x + 2);
        stream4.limit(30).forEach(System.out::println);
        Stream.generate(() -> Math.random()).limit(10).forEach(System.out::println);
    }

中間操做

多箇中間操做鏈接起來就是一個流水線,除非流水線上觸發終止操做,不然中間操做不會執行任何的處理,而在終止操做時一次性所有處理,稱爲「惰性求值」。

  • 篩選、切片
  • 映射
  • 排序

篩選、切片 方法 | 描述 ---|--- filter(Predicate<? super T> predicate) | 篩選,接收 Lambda,從流中排除某些元素 distinct() | 篩選,經過流所生成元素的 hashCode() 和 equals() 去除重複元素 limit(long maxSize) | 截斷流,使其元素不超過給定數量 skip(long n) | 跳過元素,返回一個扔掉了前 n 個元素的流。若流中元素不足 n 個,則返回一個空流。<br/> 與 limit(n) 互補。

映射 方法 | 描述 ---|--- map(Function f) | 接收一個函數做爲參數,該函數會被應用到每一個元素上,並將其映射成一個新的元素 mapToDouble(ToDoubleFunction f) | 接收一個函數做爲參數,該函數會被應用到每一個元素上,產生一個新的 DoubleStream mapToInt(ToIntFunction f) | 接收一個函數做爲參數,該函數會被應用到每一個元素上,產生一個新的 IntStream mapToLong(ToLongFunction f) | 接收一個函數做爲參數,該函數會被應用到每一個元素上,產生一個新的 LongStream flatMap(Function f) | 接收一個函數做爲參數,將流中的每一個值都換成另外一個流,而後將全部流鏈接成一個流

總結:map() 和 flatMap() 的區別有點像集合中的 add() addAll()的區別

排序 方法 | 描述 ---|--- sorted() | 產生一個新流,其中按天然順序排序(Comparable) sorted(Comparator comp) | 產生一個新流,其中按比較器順序排序(Comparator)

示例:

List<Employee> employeeList = Arrays.asList(
            new Employee(101, "張三", 19, 3333.33),
            new Employee(102, "李四", 39, 5555.77),
            new Employee(103, "王五", 29, 9999.88),
            new Employee(104, "李二狗", 49, 3333.33),
            new Employee(105, "秦二春", 59, 9999.99)
    );
    // 中間操做
    @Test
    public void test2(){
        Stream<Employee> stream = employeeList.stream().filter(e -> e.getAge() > 20).limit(1);
        stream.forEach(System.out::println);// 終止操做
        
        List<String> stringList = Arrays.asList("aaa", "bbb", "ccc");
        stringList.stream().map(str -> str.toUpperCase()).forEach(System.out::println);// map
        employeeList.stream().map(Employee::getName).forEach(System.out::println);// 注意Employee::getName寫法
        
        stringList.stream().sorted().forEach(System.out::println);// 天然排序
        employeeList.stream().sorted((e1,e2)->{// 定製排序
            if (e1.getAge().equals(e2.getAge())) {
                return e1.getName().compareTo(e2.getName());
            }else {
                return e1.getAge().compareTo(e2.getAge());
            }
        }).forEach(System.out::println);
    }

終止操做

終止操做會從流的流水生成結果。其結果能夠是任何不是流的值,例如:List、Integet,甚至是 void。

查找與匹配 方法 | 描述 ---|--- allMatch(Predicate p) | 檢查是否匹配全部元素 anyMatch(Predicate p) | 檢查是否至少匹配一個元素 noneMatch(Predicate p) | 檢查是否沒有匹配全部元素 findFirst() | 返回第一個元素 findAny() | 返回當前流中任意元素

歸約 方法 | 描述 ---|--- reduce(T identity, BinaryOperator<T> accumulator) | 將流中元素反覆結合起來,獲得一個值。 reduce(BinaryOperator<T> accumulator) |

收集 方法 | 描述 ---|--- collect(Collector collector) | Collectors工具類提供了不少靜態方法來操做集合 collect(Supplier supplier, BiConsumer accumulator,BiConsumer combiner) |

示例:

// 終止操做
@Test
public void test3(){
    long count = employeeList.stream().count();
    Optional<Employee> op1 = employeeList.stream()
            .max((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
    System.out.println(op1.get().getSalary());
    Optional<Double> op2 = employeeList.stream().map(Employee::getSalary).min(Double::compareTo);
    System.out.println(op2.get());
    //歸約
    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    // identity 是起始值,開始時,x=identity=0,而後x做爲和,y是list中的下一個元素,至關因而 x += y 操做
    Integer sum = list.stream().reduce(0, (x, y) -> x + y);
    System.out.println(sum);
    Optional<Double> opt3 = employeeList.stream().map(Employee::getSalary).reduce(Double::sum);
    System.out.println(opt3.get());
    //收集
    List<String> list2 = employeeList.stream().map(Employee::getName).collect(Collectors.toList());
    list2.forEach(System.out::println);
    employeeList.stream().map(Employee::getName).collect(Collectors.toSet()).forEach(System.out::print);//set能夠去重
    employeeList.stream().map(Employee::getName).collect(Collectors.toCollection(HashSet::new)).forEach(System.out::print);//收集到 hashset
    Long count1 = employeeList.stream().collect(Collectors.counting());//總數
    Double avg1 = employeeList.stream().collect(Collectors.averagingDouble(Employee::getSalary));//求平均
    System.out.println(avg1);
    Optional<Double> opt4 = employeeList.stream().map(Employee::getSalary).collect(Collectors.maxBy(Double::compare));//最大值
    Optional<Double> opt5 = employeeList.stream().map(Employee::getSalary).collect(Collectors.minBy(Double::compare));//最小值
    System.out.println(opt4.get());
    Map<Integer, List<Employee>> map1 = employeeList.stream().collect(Collectors.groupingBy(Employee::getAge));//分組
    System.out.println(map1);
    Map<String, Map<Integer, List<Employee>>> map2 = employeeList.stream()
            .collect(Collectors.groupingBy(Employee::getName, Collectors.groupingBy(Employee::getAge)));//多級分組,先按姓名,再按年齡
    System.out.println(map2);
    Map<Boolean, List<Employee>> map3 = employeeList.stream()
            .collect(Collectors.partitioningBy((e) -> e.getSalary() > 5000));//分區,按工資大於5000分區
    System.out.println(map3);
    DoubleSummaryStatistics dss = employeeList.stream()
            .collect(Collectors.summarizingDouble(Employee::getSalary));//彙總,跟上面求最大、最小、數量功能相同
    System.out.println(dss.getMax());
    System.out.println(dss.getMin());
    System.out.println(dss.getCount());
    String str1 = employeeList.stream().map(Employee::getName).collect(Collectors.joining(","));//鏈接,名字的拼接
    System.out.println(str1);
}

map 和 reduce 的鏈接一般稱爲 map-reduce 模式,因Google用它來進行網絡搜索而出名。

API練習

1)給定一個數字列表,如何返回一個由每一個數的平方構成的列表?如,給定【1,2,3,4,5】,返回【1,4,9,16,25】

@Test
    public void test4(){
        Integer[] intArr = {1, 2, 3, 4, 5};
        Arrays.stream(intArr).map((x) -> x * x).forEach(System.out::print);
    }

2)怎樣用 map 和 reduce 方法統計流中有多少個 Employee ?

@Test
    public void test5() {
        Optional<Integer> opt1 = employeeList.stream().map(e -> 1).reduce(Integer::sum);//亮點:map(e -> 1)
        System.out.println(opt1.get());
    }

3)練習題

//交易員類
public class Trader {

	private String name;
	private String city;

	public Trader() {
	}

	public Trader(String name, String city) {
		this.name = name;
		this.city = city;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getCity() {
		return city;
	}

	public void setCity(String city) {
		this.city = city;
	}

	@Override
	public String toString() {
		return "Trader [name=" + name + ", city=" + city + "]";
	}
}
//交易類
public class Transaction {

	private Trader trader;
	private int year;
	private int value;

	public Transaction() {
	}

	public Transaction(Trader trader, int year, int value) {
		this.trader = trader;
		this.year = year;
		this.value = value;
	}

	public Trader getTrader() {
		return trader;
	}

	public void setTrader(Trader trader) {
		this.trader = trader;
	}

	public int getYear() {
		return year;
	}

	public void setYear(int year) {
		this.year = year;
	}

	public int getValue() {
		return value;
	}

	public void setValue(int value) {
		this.value = value;
	}

	@Override
	public String toString() {
		return "Transaction [trader=" + trader + ", year=" + year + ", value="
				+ value + "]";
	}
}

練習題:

public class TransactionTest {
    List<Transaction> transactions = null;

    @Before
    public void before(){
        Trader raoul = new Trader("Raoul", "Cambridge");
        Trader mario = new Trader("Mario", "Milan");
        Trader alan = new Trader("Alan", "Cambridge");
        Trader brian = new Trader("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)
        );
    }
    //1. 找出2011年發生的全部交易, 並按交易額排序(從低到高)
    //2. 交易員都在哪些不一樣的城市工做過?
    //3. 查找全部來自劍橋的交易員,並按姓名排序
    //4. 返回全部交易員的姓名字符串,按字母順序排序
    //5. 有沒有交易員是在米蘭工做的?
    //6. 打印生活在劍橋的交易員的全部交易額
    //7. 全部交易中,最高的交易額是多少
    //8. 找到交易額最小的交易
}

參考:https://github.com/wangpw2016/java8-day02/blob/master/src/com/atguigu/exer/TestTransaction.java

public class TransactionTest {
    List<Transaction> transactions = null;

    @Before
    public void before() {
        Trader raoul = new Trader("Raoul", "Cambridge");
        Trader mario = new Trader("Mario", "Milan");
        Trader alan = new Trader("Alan", "Cambridge");
        Trader brian = new Trader("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)
        );
    }

    //1. 找出2011年發生的全部交易, 並按交易額排序(從低到高)
    @Test
    public void test1() {
        transactions.stream()
                .filter(e -> e.getYear() == 2011)
                .sorted((e1, e2) -> Integer.compare(e1.getValue(), e2.getValue()))
                .forEach(System.out::println);
    }

    //2. 交易員都在哪些不一樣的城市工做過?
    @Test
    public void test2() {
        transactions.stream()
                .map(Transaction::getTrader)
                .map(Trader::getCity)
                .collect(Collectors.toSet())
                .forEach(System.out::println);
    }
    //3. 查找全部來自劍橋的交易員,並按姓名排序
    @Test
    public void test3(){
        transactions.stream()
                .map(Transaction::getTrader)
                .filter(e -> e.getCity().equals("Cambridge"))
                .sorted((e1,e2) -> e1.getName().compareTo(e2.getName()))
                .distinct()// 去重
                .forEach(System.out::println);
    }
    //4. 返回全部交易員的姓名字符串,按字母順序排序
    @Test
    public void test4(){
        transactions.stream()
                .map(Transaction::getTrader)
                .map(Trader::getName)
                .sorted()
                .distinct()
                .forEach(System.out::println);
        String str = transactions.stream()
                .map(Transaction::getTrader)
                .map(Trader::getName)
                .sorted().distinct()
                .reduce("", String::concat);//重點,歸約
        System.out.println(str);
    }

    //5. 有沒有交易員是在米蘭工做的?
    @Test
    public void test5(){
        boolean b = transactions.stream()
                .anyMatch(e -> "Milan".equals(e.getTrader().getCity()));
        System.out.println(b);
    }
    //6. 打印生活在劍橋的交易員的全部交易額
    @Test
    public void test6(){
        // 方式一
        IntSummaryStatistics valSum = transactions.stream()
                .filter(t -> "Cambridge".equals(t.getTrader().getCity()))
                .collect(Collectors.summarizingInt(Transaction::getValue));
        System.out.println(valSum.getSum());
        // 方式二
        Optional<Integer> valSum2 = transactions.stream()
                .filter(t -> "Cambridge".equals(t.getTrader().getCity()))
                .map(Transaction::getValue)
                .reduce(Integer::sum);
        System.out.println(valSum2.get());
    }
    //7. 全部交易中,最高的交易額是多少
    @Test
    public void test7(){
        // 方式一
        Optional<Transaction> opt = transactions.stream()
                .collect(Collectors.maxBy((e1, e2) -> Integer.compare(e1.getValue(), e2.getValue())));
        System.out.println(opt.get().getValue());
        // 方式二
        Optional<Integer> opt2 = transactions.stream()
                .map(Transaction::getValue)
                .max(Integer::compare);
        System.out.println(opt2.get());
    }
    //8. 找到交易額最小的交易(記錄)
    @Test
    public void test8(){
        Optional<Transaction> opt = transactions.stream()
                .min((e1, e2) -> Integer.compare(e1.getValue(), e2.getValue()));
        System.out.println(opt.get());
    }


    //1. 找出2011年發生的全部交易, 並按交易額排序(從低到高)
    //2. 交易員都在哪些不一樣的城市工做過?
    //3. 查找全部來自劍橋的交易員,並按姓名排序
    //4. 返回全部交易員的姓名字符串,按字母順序排序
    //5. 有沒有交易員是在米蘭工做的?
    //6. 打印生活在劍橋的交易員的全部交易額
    //7. 全部交易中,最高的交易額是多少
    //8. 找到交易額最小的交易
}

Optional

java.util.Optional 是一個容器類,表明一個值存在或不存在,能夠避免空指針異常。

經常使用方法:

  • Optional.of(T t);建立一個 Optional 實例
  • Optional.empty();建立一個空的 Optional 實例
  • Optional.ofNullable(T t);若 T 不爲 null ,建立 Optional 實例,不然建立空實例
  • isPresent();判斷是否包含值
  • orElse(T t);若是調用對象包含值,返回該值,不然返回 t
  • orElseGet(Supplier<? extends T> s);若是調用對象包含值,返回該值,不然返回 s 獲取的值
  • map(Function<? super T, ? extends U> mapper);若是有值,對其處理並返回處理後的 Optional,不然返回Optional.empty();
  • flatMap(Function<? super T, Optional<U>> mapper);與 map 相似,要求返回值必須是 Optional

使用示例:http://www.javashuo.com/article/p-umyectap-a.html

並行流 & 順序流

並行流就是把一個內容分紅多個數據塊,並用不一樣的線程分別處理每一個數據塊的流。

Java8中將並行流進行了優化,Stream API 能夠聲明性經過 parallel() 與 sequential() 在並行流與順序流之間切換。

瞭解 Fork/Join 框架,大任務分爲小任務,能夠將結果彙總或沒有返回值;

java7中關於 Fork/Join 的寫法,參考《Java-Se-多線程-1》。

採用「工做竊取」模式(work-stealing):當執行新的任務時它能夠將其拆分紅更小的任務執行,並將小任務加到線程隊列中,而後再從一個隨機的隊列中偷一個並把他放在本身的隊列中。

// java8
    @Test
    public void test(){
        Instant start = Instant.now();

        LongStream.rangeClosed(0, 100000000000L)
                .parallel()
                .reduce(0, Long::sum);

        Instant end = Instant.now();
        System.out.println("耗費時間:"+Duration.between(start,end).toMillis()+" 毫秒");// 34737
    }

可重複註解、類型註解

解析註解:

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息