Java8新特性(lambda、Stream、Optional)
阿新 • • 發佈:2019-02-14
1、λ表示式
lambda表示式:λ表示式是Java8新增的一個特性,《Core Java》中的對它的解析是——“一個可傳遞的程式碼塊,可以在以後執行一次或多次”。- 從日常開發的角度來看,它可以簡化我們的很多程式碼(當然不止這一個原因),特別是很多匿名內部類的寫法都可以被λ表示式替換成一個語句。
- λ表示式從本質上來看是語法糖,但它並不是簡單的匿名內部類的語法糖,λ表示式的內部實現機制也都不是採用匿名內部類,說到底還是效能原因。
- 對於大多數情況來說,Lambda表示式要比匿名內部類效能更優。
// 使用匿名類的方式新建一個執行緒 new Thread(new Runnable() { @Override public void run() { System.out.println("Hello world !"); } }).start(); // 使用λ表示式的方式新建一個執行緒 new Thread(() -> System.out.println("Hello world !")).start();
lambda表示式的語法:
(parameters) → expression
或者
(parameters) → { statements; } // 這種方式可以寫多條語句
lambda表示式的使用場景:
- 匿名內部類(上文已展示)
- 集合排序
- 結合stream使用
public class Human { private String name; private int age; public Human() { super(); } // standard getters and setters…… } // 使用傳統的方式對集合進行排序:對集合進行排序要為Comparator建立一個匿名內部類用來排序: new Comparator<Human>() { @Override public int compare(Human h1, Human h2) { return h1.getName().compareTo(h2.getName()); } } @Test public void givenPreLambda_whenSortingEntitiesByName_thenCorrectlySorted() { List<Human> humans = Lists.newArrayList(new Human("Sarah", 10), new Human("Jack", 12)); Collections.sort(humans, new Comparator<Human>() { @Override public int compare(Human h1, Human h2) { return h1.getName().compareTo(h2.getName()); } }); Assert.assertThat(humans.get(0), equalTo(new Human("Jack", 12))); } // 使用λ表示式方式 @Test public void whenSortingEntitiesByName_thenCorrectlySorted() { List<Human> humans = Lists.newArrayList(new Human("Sarah", 10), new Human("Jack", 12)); humans.sort((Human h1, Human h2) -> h1.getName().compareTo(h2.getName())); Assert.assertThat(humans.get(0), equalTo(new Human("Jack", 12))); }
2、Stream
Stream(流)是Java8的新增的一個庫,它最常見的用法是對集合中的資料進行操作,它就像一個高階版的Iterator,使用者只需要定義好對元素的操作,並將操作應用到元素上就好,而具體的實現演算法則交給類庫的設計者,這樣就很方便的將操作和演算法分開。所有流計算都有一種共同的結構:它們具有一個流來源、0 或多箇中間操作,以及一個終止操作。流的元素可以是物件引用 (Stream<String>),也可以是原始整數 (IntStream)、長整型 (LongStream) 或雙精度 (DoubleStream)。建立stream的方式:// 使用傳統的for迴圈的方式: // 將每個userId查詢到的記錄放在List<UserBaseInfo>中 for (UserBaseInfo userBaseInfo : userBaseInfoList) { userId = userBaseInfo.getUserId(); if (userIDandBaseInfoMap.containsKey(userId)) { userBaseInfos = userIDandBaseInfoMap.get(userId); } else { userBaseInfos = new ArrayList<>(); userIDandBaseInfoMap.put(userId, userBaseInfos); } userBaseInfos.add(userBaseInfo); } // 使用stream進行操作 Map<Long, List<UserBaseInfo>> userIDandBaseInfoMap = userBaseInfoList.stream() .collect(Collectors.groupingBy(UserBaseInfo::getUserId)); // 說明:Collection介面的預設方法建立流
- Stream介面的靜態工廠方法of (Java8裡介面可以帶靜態方法)
- Collection介面預設方法或子類獲取流(Java8對Collection的增強)(見上文例子)
// stream介面的of方法有兩個過載的方法
Stream<String> stringStream = Stream.of("youzan");
Stream<Integer> integerStream = Stream.of(1, 2, 3, 5);
// 使用變長引數建立流返回的是一個Arrays的流(原始碼)
@SafeVarargs
@SuppressWarnings("varargs") // Creating a stream from an array is safe
public static<T> Stream<T> of(T... values) {
return Arrays.stream(values);
}
// generator方法的方式
Stream.generate(new Supplier<Double>() {
@Override
public Double get() {
return Math.random();
}
});
Stream.generate(() -> Math.random());
Stream.generate(Math::random);
Java8對Collection介面的增強
public interface Collection<E> extends Iterable<E> {
default Stream<E> stream() { // 介面中新增了Stream方法,讓集合類可以很方便的建立流
return StreamSupport.stream(spliterator(), false);
}
}
使用Stream介面建立流:// stream介面的of方法有兩個過載的方法
Stream<String> stringStream = Stream.of("youzan");
Stream<Integer> integerStream = Stream.of(1, 2, 3, 5);
// 使用變長引數建立流返回的是一個Arrays的流(原始碼)
@SafeVarargs
@SuppressWarnings("varargs") // Creating a stream from an array is safe
public static<T> Stream<T> of(T... values) {
return Arrays.stream(values);
}
// generator方法的方式
Stream.generate(new Supplier<Double>() {
@Override
public Double get() {
return Math.random();
}
});
Stream.generate(() -> Math.random());
Stream.generate(Math::random);
流的來源:方法 | 描述 |
---|---|
Collection.stream() | 使用一個集合的元素建立一個流 |
Stream.of(T...) | 使用傳遞給工廠方法的引數建立一個流 |
Stream.of(T[]) | 使用一個數組的元素建立一個流 |
Stream.empty() | 建立一個空流。 |
Stream.iterate(T first, BinaryOperator<T> f) | 建立一個包含序列 first, f(first), f(f(first)), ... 的無限流 |
Stream.generate(Supplier<T> f) | 使用一個生成器函式建立一個無限流 |
IntStream.range(lower, upper) | 建立一個由下限到上限(不含)之間的元素組成的IntStream |
IntStream.rangeClosed(lower, upper) | 建立一個由下限到上限(含)之間的元素組成的 IntStream |
BufferedReader.lines() | 建立一個有來自 BufferedReader 的行組成的流 |
BitSet.stream() | 建立一個由 BitSet 中的設定位的索引組成的 IntStream |
Stream.chars() | 建立一個與 String 中的字元對應的 IntStream |
流的中間操作:
操作 | 內容 |
---|---|
filter(Predicate<T>) | 與預期匹配的流的元素 |
map(Function<T, U>) | 將提供的函式應用於流的元素的結果 |
flatMap(Function<T, Stream<U>> | 將提供的流處理函式應用於流元素後獲得的流元素 |
distinct() | 已刪除了重複的流元素 |
sorted() | 按自然順序排序的流元素 |
Sorted(Comparator<T>) | 按提供的比較符排序的流元素 |
limit(long) | 截斷至所提供長度的流元素 |
skip(long) | 丟棄了前 N 個元素的流元素 |
takeWhile(Predicate<T>) | (僅限 Java 9)在第一個提供的預期不是 true 的元素處階段的流元素 |
dropWhile(Predicate<T>) | (僅限 Java 9)丟棄了所提供的預期為 true 的初始元素分段的流元素 |
流的終止操作:
操作 | 內容 |
---|---|
forEach(Consumer<T> action) | 將提供的操作應用於流的每個元素 |
toArray() | 使用流的元素建立一個數組 |
reduce(...) | 將流的元素聚合為一個彙總值 |
collect(...) | 將流的元素聚合到一個彙總結果容器中 |
min(Comparator<T>) | 通過比較符返回流的最小元素 |
max(Comparator<T>) | 通過比較符返回流的最大元素 |
count() | 返回流的大小 |
{any,all,none}Match(Predicate<T>) | 返回流的任何/所有元素是否與提供的預期相匹配 |
findFirst() | 返回流的第一個元素(如果有) |
findAny() | 返回流的任何元素(如果有) |
3、Optional
Optional:Optional是Java8新引進的一個允許為null的容器物件,它的到來可以使程式設計師減少與NullPointException(NPE)打交道的次數。Optional提供了一種優雅的Java風格的方法來解決null安全問題。// Java8之前想獲取一個實體的某一個屬性是這樣的
public String getName(Employee em) {
if (em == null) {
return "Unknow employee name";
}
return em.getName();
}
// 使用Optional優雅的獲取
public String getName(Employee em) {
Optional.ofNullable(em).map(employee -> employee.getName()).orElse("Unknow employee name");
}
建立Optional物件:
- of方法
- ofNullable方法(推薦)
// 使用of方法,但是它仍然有可能報NPE
Optional<String> optional = Optional.of("youzan");
// 使用of方法,不會報NPE
Optional<String> optional = Optional.ofNullable("youzan");
// 通過原始碼來看為什麼of方法會報NPE
public static <T> Optional<T> of(T value) {
return new Optional<>(value); // 新建一個物件
}
// 呼叫顯式建構函式去新建物件
private Optional(T value) {
this.value = Objects.requireNonNull(value);
}
// Objects工具類的requireNonNull方法遇到null會報NPE
public static <T> T requireNonNull(T obj) {
if (obj == null)
throw new NullPointerException();
return obj;
}
// 接下來還是通過原始碼看看ofNullable方法是怎麼樣做到允許null的
public static <T> Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value); // 如果value為null則呼叫empty方法
}
// 隱藏在empty方法中的祕密
public static<T> Optional<T> empty() {
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY; // 返回一個EMPTY物件
return t;
}
// EMPTY的廬山真面目
private static final Optional<?> EMPTY = new Optional<>();
// Optional提供的無參建構函式將返回一個值空的Optional物件
private Optional() {
this.value = null; // value是用來儲存物件的值的
}
orElse方法:物件的值為null則返回引數傳進來的值
// 使用姿勢
String string = Optional.ofNullable("youzan").orElse("Unknow");
// orElse方法揭祕
public T orElse(T other) {
return value != null ? value : other; // 如果當前的Optional物件為null則返回other
}
orElseGet方法:orElseGet與orElse方法類似,區別在於得到的預設值。orElse方法將傳入的字串作為預設值,orElseGet方法可以接受Supplier介面的實現用來生成預設值。
// orElseGet方法原始碼
public T orElseGet(Supplier<? extends T> other) {
return value != null ? value : other.get();
}
orElseThrow方法:如果物件值為null則丟擲一個異常
// 使用姿勢
Optional.ofNullable(object).orElseThrow(NoSuchElementException::new);
// orElseThrow方法原始碼
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
if (value != null) {
return value;
} else {
throw exceptionSupplier.get(); // 嗯,這裡能幫你丟擲一個異常就是了
}
}
map方法:map方法用來對Optional例項的值執行一系列操作。通過一組實現了Function介面的lambda表示式傳入操作。// 使用姿勢
Optional<String> upperName = name.map((value) -> value.toUpperCase());
// map方法原始碼
public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
Objects.requireNonNull(mapper); // 如果形參mapper為null,則報NPE
if (!isPresent())
return empty(); // 返回一個不包含值的Optional物件
else {
return Optional.ofNullable(mapper.apply(value));
}
}
// isPresent方法是檢視當前optional物件的值是否有值
public boolean isPresent() {
return value != null;
}
flatMap方法:flatMap方法與map方法類似,區別在於mapping函式的返回值不同。map方法的mapping函式返回值可以是任何型別T,而flatMap方法的mapping函式必須是Optional。
filter方法:filter個方法通過傳入限定條件對Optional例項的值進行過濾。
// filter方法檢查給定的Option值是否滿足某些條件。
// 如果滿足則返回同一個Option例項,否則返回空Optional。
Optional<String> name = Optional.of("Sana");
Optional<String> longName = name.filter((value) -> value.length() > 6);
System.out.println(longName.orElse("The name is less than 6 characters")); // 輸出Sanaulla