JAVA8新特性用法
JAVA8新特性各種用法詳解
訪問介面的預設方法
Lambda表示式中是無法訪問到預設方法的,以下程式碼將無法編譯:
複製程式碼 程式碼如下:
Formula formula = (a) -> sqrt( a * 100);
Built-in Functional Interfaces
JDK 1.8 API包含了很多內建的函式式介面,在老Java中常用到的比如Comparator或者Runnable介面,這些介面都增加了@FunctionalInterface註解以便能用在lambda上。
Java 8 API同樣還提供了很多全新的函式式介面來讓工作更加方便,有一些介面是來自Google Guava庫裡的,即便你對這些很熟悉了,還是有必要看看這些是如何擴充套件到lambda上使用的。
Predicate介面
Predicate 介面只有一個引數,返回boolean型別。該介面包含多種預設方法來將Predicate組合成其他複雜的邏輯(比如:與,或,非):
複製程式碼 程式碼如下:
Predicate<String> predicate = (s) -> s.length() > 0;
predicate.test("foo");
predicate.negate().test("foo");
Predicate<Boolean> nonNull = Objects::nonNull;
Predicate<Boolean> isNull = Objects::isNull;
Predicate<String> isEmpty = String::isEmpty;
Predicate<String> isNotEmpty = isEmpty.negate();
Function 介面
Function 介面有一個引數並且返回一個結果,並附帶了一些可以和其他函式組合的預設方法(compose, andThen):
複製程式碼 程式碼如下:
Function<String, Integer> toInteger = Integer::valueOf;
Function<String, String> backToString = toInteger.andThen(String::valueOf);
backToString.apply("123"); // "123"
Supplier 介面
Supplier 介面返回一個任意範型的值,和Function介面不同的是該介面沒有任何引數
複製程式碼 程式碼如下:
Supplier<Person> personSupplier = Person::new;
personSupplier.get();
Consumer 介面
Consumer 介面表示執行在單個引數上的操作。
複製程式碼 程式碼如下:
Consumer<Person> greeter = (p) -> System.out.println("Hello, " + p.firstName)
greeter.accept(new Person("Luke", "Skywalker"))
Comparator 介面
Comparator 是老Java中的經典介面, Java 8在此之上新增了多種預設方法:
複製程式碼 程式碼如下:
Comparator<Person> comparator = (p1, p2) -> p1.firstName.compareTo(p2.firstName)
Person p1 = new Person("John", "Doe")
Person p2 = new Person("Alice", "Wonderland")
comparator.compare(p1, p2)
comparator.reversed().compare(p1, p2)
Optional 介面
Optional 不是函式是介面,這是個用來防止NullPointerException異常的輔助型別,這是下一屆中將要用到的重要概念,現在先簡單的看看這個介面能幹什麼:
Optional 被定義為一個簡單的容器,其值可能是null或者不是null。在Java 8之前一般某個函式應該返回非空物件但是偶爾卻可能返回了null,而在Java 8中,不推薦你返回null而是返回Optional。
複製程式碼 程式碼如下:
Optional<String> optional = Optional.of("bam");
optional.isPresent(); // true
optional.get(); // "bam"
optional.orElse("fallback"); // "bam"
optional.ifPresent((s) -> System.out.println(s.charAt(0))); // "b"
Stream 介面
java.util.Stream 表示能應用在一組元素上一次執行的操作序列。Stream 操作分為中間操作或者最終操作兩種,最終操作返回一特定型別的計算結果,而中間操作返回Stream本身,這樣你就可以將多個操作依次串起來。Stream 的建立需要指定一個資料來源,比如 java.util.Collection的子類,List或者Set, Map不支援。Stream的操作可以序列執行或者並行執行。
首先看看Stream是怎麼用,首先建立例項程式碼的用到的資料List:
複製程式碼 程式碼如下:
List<String> stringCollection = new ArrayList<>()
stringCollection.add("ddd2")
stringCollection.add("aaa2")
stringCollection.add("bbb1")
stringCollection.add("aaa1")
stringCollection.add("bbb3")
stringCollection.add("ccc")
stringCollection.add("bbb2")
stringCollection.add("ddd1")
Java 8擴充套件了集合類,可以通過 Collection.stream() 或者 Collection.parallelStream() 來建立一個Stream。下面幾節將詳細解釋常用的Stream操作:
Filter 過濾
過濾通過一個predicate介面來過濾並只保留符合條件的元素,該操作屬於中間操作,所以我們可以在過濾後的結果來應用其他Stream操作(比如forEach)。forEach需要一個函式來對過濾後的元素依次執行。forEach是一個最終操作,所以我們不能在forEach之後來執行其他Stream操作。
複製程式碼 程式碼如下:
stringCollection
.stream()
.filter((s) -> s.startsWith("a"))
.forEach(System.out::println)
// "aaa2", "aaa1"
Sort 排序
排序是一箇中間操作,返回的是排序好後的Stream。如果你不指定一個自定義的Comparator則會使用預設排序。
複製程式碼 程式碼如下:
stringCollection
.stream()
.sorted()
.filter((s) -> s.startsWith("a"))
.forEach(System.out::println)
// "aaa1", "aaa2"
需要注意的是,排序只建立了一個排列好後的Stream,而不會影響原有的資料來源,排序之後原資料stringCollection是不會被修改的:
複製程式碼 程式碼如下:
System.out.println(stringCollection);
Map 對映
中間操作map會將元素根據指定的Function介面來依次將元素轉成另外的物件,下面的示例展示了將字串轉換為大寫字串。你也可以通過map來講物件轉換成其他型別,map返回的Stream型別是根據你map傳遞進去的函式的返回值決定的。
複製程式碼 程式碼如下:
boolean anyStartsWithA = stringCollection
.stream()
.map(String::toUpperCase)
.sorted((a, b) -> b.compareTo(a))
.forEach(System.out::println)
// "DDD2", "DDD1", "CCC", "BBB3", "BBB2", "AAA2", "AAA1"
Match 匹配
Stream提供了多種匹配操作,允許檢測指定的Predicate是否匹配整個Stream。所有的匹配操作都是最終操作,並返回一個boolean型別的值。
複製程式碼 程式碼如下:
boolean anyStartsWithA =
stringCollection
.stream()
.anyMatch((s) -> s.startsWith("a"));
System.out.println(anyStartsWithA); // true
boolean allStartsWithA =
stringCollection
.stream()
.allMatch((s) -> s.startsWith("a"));
System.out.println(allStartsWithA); // false
boolean noneStartsWithZ =
stringCollection
.stream()
.noneMatch((s) -> s.startsWith("z"));
System.out.println(noneStartsWithZ); // true
Count 計數
計數是一個最終操作,返回Stream中元素的個數,返回值型別是long。
複製程式碼 程式碼如下:
long startsWithB =
stringCollection
.stream()
.filter((s) -> s.startsWith("b"))
.count();
System.out.println(startsWithB); // 3
Reduce 規約
這是一個最終操作,允許通過指定的函式來講stream中的多個元素規約為一個元素,規越後的結果是通過Optional介面表示的:
複製程式碼 程式碼如下:
Optional<String> reduced =
stringCollection
.stream()
.sorted()
.reduce((s1, s2) -> s1 + "#" + s2)
reduced.ifPresent(System.out::println)
// "aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2"
並行Streams
前面提到過Stream有序列和並行兩種,序列Stream上的操作是在一個執行緒中依次完成,而並行Stream則是在多個執行緒上同時執行。
下面的例子展示了是如何通過並行Stream來提升效能:
首先我們建立一個沒有重複元素的大表:
複製程式碼 程式碼如下:
int max = 1000000;
List<String> values = new ArrayList<>(max);
for (int i = 0; i < max; i++) {
UUID uuid = UUID.randomUUID();
values.add(uuid.toString());
}
然後我們計算一下排序這個Stream要耗時多久,
序列排序:
複製程式碼 程式碼如下:
long t0 = System.nanoTime()
long count = values.stream().sorted().count()
System.out.println(count)
long t1 = System.nanoTime()
long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0)
System.out.println(String.format("sequential sort took: %d ms", millis))
// 序列耗時: 899 ms
並行排序:
複製程式碼 程式碼如下:
long t0 = System.nanoTime()
long count = values.parallelStream().sorted().count()
System.out.println(count)
long t1 = System.nanoTime()
long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0)
System.out.println(String.format("parallel sort took: %d ms", millis))
// 並行排序耗時: 472 ms
上面兩個程式碼幾乎是一樣的,但是並行版的快了50%之多,唯一需要做的改動就是將stream()改為parallelStream()。
Map
前面提到過,Map型別不支援stream,不過Map提供了一些新的有用的方法來處理一些日常任務。
複製程式碼 程式碼如下:
Map<Integer, String> map = new HashMap<>();
for (int i = 0; i < 10; i++) {
map.putIfAbsent(i, "val" + i);
}
map.forEach((id, val) -> System.out.println(val));
以上程式碼很容易理解, putIfAbsent 不需要我們做額外的存在性檢查,而forEach則接收一個Consumer介面來對map裡的每一個鍵值對進行操作。
下面的例子展示了map上的其他有用的函式:
複製程式碼 程式碼如下:
map.computeIfPresent(3, (num, val) -> val + num);
map.get(3); // val33
map.computeIfPresent(9, (num, val) -> null);
map.containsKey(9); // false
map.computeIfAbsent(23, num -> "val" + num);
map.containsKey(23); // true
map.computeIfAbsent(3, num -> "bam");
map.get(3); // val33
接下來展示如何在Map裡刪除一個鍵值全都匹配的項:
複製程式碼 程式碼如下:
map.remove(3, "val3");map.get(3);
map.remove(3, "val33");map.get(3);
另外一個有用的方法:
複製程式碼 程式碼如下:
map.getOrDefault(42, "not found"); // not found
對Map的元素做合併也變得很容易了:
複製程式碼 程式碼如下:
map.merge(9, "val9", (value, newValue) -> value.concat(newValue));
map.get(9); // val9
map.merge(9, "concat", (value, newValue) -> value.concat(newValue));
map.get(9);// val9concat
Merge做的事情是如果鍵名不存在則插入,否則則對原鍵對應的值做合併操作並重新插入到map中。
labmel表示式在toMap的時候如果是null的會報錯
null針的判斷
日誌列印的時候最好也是在不是空的時候列印,要不然就null針了
grouping
Stream流的方式能夠讓我們在程式碼層還錦上添花,不用寫的那麼複雜難懂。
像這個grouping就是我們SQL語句裡面的group by 分組嘛
而Java8這裡就可以在程式碼裡面根據不同的東西來分組。
例如:
Map<String, List<User>> collect = list.stream()
.collect(
Collectors.groupingBy(
User::getName/*, Collectors.counting()*/
)
);
相關文章
- JAVA8新特性Java
- Java8 新特性Java
- Java8新特性 - LambdaJava
- Java8的新特性Java
- Java8新特性之:OptionalJava
- Java8新特性--Stream APIJavaAPI
- java8新特性stream流Java
- Java8新特性實踐Java
- Java8新特性系列-LambdaJava
- Java8新特性系列(Stream)Java
- Java8新特性系列(Interface)Java
- Java8新特性系列(Lambda)Java
- Java8 新特性詳解Java
- java8新特性學習Java
- ?Java8新特性之Optional類Java
- java8 新特性之方法引用Java
- java8 新特性之Optional 類Java
- Java8 新特性之 Optional 類Java
- Java8的八個新特性Java
- Java8新特性(1):Lambda表示式Java
- Java8常用的新特性總結Java
- Java8新特性(一)-Lambda表示式Java
- java8 新特性之Lambda 表示式Java
- java8 新特性之預設方法Java
- Java8新特性探索之Stream介面Java
- Java8新特性系列(原子性操作)Java
- Java8 新特性(一)- 介面增強Java
- Java8 新特性之 Lambda 表示式Java
- Java8新特性都到碗裡來Java
- 【java8新特性】蘭姆達表示式Java
- JAVA8 新特性(二)轉載整理Java
- Java8新特性之時間APIJavaAPI
- Java8的新特性--函式式介面Java函式
- Java8新特性之日期-時間APIJavaAPI
- java8 新特性之函式式介面Java函式
- java8 新特性之日期時間 APIJavaAPI
- Java8 新特性 —— Stream 流式程式設計Java程式設計
- java8新特性之lambda表示式(一)Java