[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);
若是調用對象包含值,返回該值,不然返回 torElseGet(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 }
可重複註解、類型註解
解析註解: