Java 8 的一些新特性

函數式接口

Predicate

java.util.function.Predicatejava

@FunctionalInterface
public interface Predicate<T> {   
    // 函數式接口,布爾返回值
  	boolean test(T t);

    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

    default Predicate<T> negate() {
        return (t) -> !test(t);
    }

    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
}
複製代碼

Consumer

java.util.function.Consumer數組

@FunctionalInterface
public interface Consumer<T> {
    // 函數式接口,無返回值
    void accept(T t);

    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

複製代碼

Funcion

java.util.function.Functionapp

@FunctionalInterface
public interface Function<T, R> {
    // 函數式接口,接受 T 爲參數,返回 R
    R apply(T t);

    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

    
    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

    
    static <T> Function<T, T> identity() {
        return t -> t;
    }
}
複製代碼

Supplier

java.util.function.Supplierdom

@FunctionalInterface
public interface Supplier<T> {
    // 函數式接口,返回 T,無參數
    T get();
}
複製代碼

同時爲簡單的數據類型準備了對應的函數式接口,通常是在 Predicate 加上對應的前綴,好比 double 對應的 Predicate 接口爲 DoublePredicate異步

複合 Lambda 表達式

比較器

Compartor<Person> c = Comparator.comparing(Person::getName);
複製代碼

其內部代碼調用的是 compareTo 方法ide

逆序

List<Apple> inventory = new ArrayList<>();
inventory.sort(comparing(Apple::getWeight).reversed());
複製代碼

天然序

@Test
public void testT() {
  List<Integer> list = Arrays.asList(3, 2, 1, 2, 2, 2, 5, 6, 7, 5, 4, 9, 100);
  list.stream()
    .sorted(Comparator.reverseOrder())
    .forEach(System.err::println);
}
複製代碼

調用 ComparatorreverseOrder() 方法能夠直接進行天然排序函數

比較器鏈

thenComparing ,若是對象的第一個 Compartor 比較以後是同樣的,就使用第二個 Compartorui

List<Apple> inventory = new ArrayList<>();
inventory.sort(comparing(Apple::getWeight).reversed().thenComparing(Apple::getCountry));
複製代碼

謂詞複合

  • negatethis

    表示非spa

  • and

    表示與

  • or

    表示或

優先級的肯定,從左向右。

Predicate<B.Name> predicate = (B.Name n) -> "a".equals(name.getName());
Predicate<B.Name> and = predicate.and((n) -> n.getName().endsWith("b"));
複製代碼

函數複合

  • compose

    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
      Objects.requireNonNull(before);
      return (V v) -> apply(before.apply(v));
    }
    複製代碼

    示例代碼

    Function<Integer, Integer> f = x -> x + 1;
    Function<Integer, Integer> g = x -> x * 2;
    Function<Integer, Integer> h = f.compose(g);
    int result = h.apply(1);
    複製代碼

    運算方式:f(g(x)) = x * 2 + 1 = 1 * 2 + 1 = 3

  • andThen

    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
      Objects.requireNonNull(after);
      return (T t) -> after.apply(apply(t));
    }
    複製代碼

    示例代碼:

    Function<Integer, Integer> f = x -> x + 1;
    Function<Integer, Integer> g = x -> x * 2;
    Function<Integer, Integer> h = f.andThen(g);
    int result = h.apply(1);
    複製代碼

    運算方式:g(f(x)) = (x * 1) * 2 = (1 + 1) * 2 = 4

    composeandThen 都返回 Function 對象,能夠將其進行復合。

流(Stream)

java.util.stream.Stream

定義:從支持數據處理操做的源生成的元素序列

流的特色

  • 只能遍歷一次(只能消費一次)

    流只能遍歷一次,遍歷以後,這個流就被消費掉了,不能再次使用。

  • 內部迭代

    使用 map 之類的方式進行迭代,而不是for-each 等循環方式

錯誤的示例

String str = "this is successful";
Stream<String[]> stream = Stream.of(str)
  .map(s -> s.split(" "));
stream.forEach(System.out::println);
stream.forEach(System.out::println);
複製代碼

流操做

流操做能夠分爲兩大類,中間操做和終端操做。

  • 中間操做

    中間操做會返回另外一個流,讓多個流組成一條流水線,若是沒有觸發終端操做,流不會執行。

  • 終端操做

    終端操做會從流水線上生成結果,其結果再也不是注的值。

流的使用

在使用流的時候,整個鏈是:

數據源 -> 中間操做 -> 終端操做

使用流

  • filter

  • distinct

  • limit

  • skip

  • map

  • flatMap

    與 map 的區別,會進行打散操做

  • sorted

  • anyMatch : 有一個匹配

  • allMatch : 所有匹配

  • noneMatch : 不匹配

  • findFirst : 第一個

  • findAny : 查找任意一個

reduce(歸約)

T reduce(T identity, BinaryOperator<T> accumulator);
Optional<T> reduce(BinaryOperator<T> accumulator);
<U> U reduce(U identity,BiFunction<U, ? super T, U> accumulator,BinaryOperator<U> combiner);
複製代碼

流的裝箱

boxed 方法能夠裝一個基本類型的流裝箱成包裝類型的流

IntStream intStream = menu.stream().mapToInt(Person::getAge);
Stream<Integer> stream = intStream.boxed(); // 裝箱
複製代碼

構建流

由值建立流

Stream<String> steam = Stream.of("this is a Steam");
steam.map(String::toUpperCase).forEach(System.out::println);
複製代碼

使用 Stream 的表態方法 of 來建立流。

由數組建立流

int[] arrays = {1, 2, 3, 4, 5, 6};
IntStream stream = Arrays.stream(arrays);
stream.map(x -> x * x).forEach(System.out::println);
複製代碼

文件生成流

try(Stream<String> lines =	Files.lines(Paths.get("/Users/mac/Documents/work/demo/loadbalancesuccess.zip"))) {
  lines.map(String::isEmpty)
    .forEach(System.out::println);
} catch (IOException e) {
  e.printStackTrace();
}
複製代碼

生成無限流

iterate

Stream.iterate(0, n -> n + 2)
  .limit(10)
  .forEach(System.out::println);
複製代碼

generate

Stream.generate(Math::random)
  .limit(5)
  .forEach(System.out::println);
複製代碼

iterate 與 generate 的區別:

  • iterate 方法接受一個初始值,依次應用在每一個產生新值上的 Lambda

  • generate 不是依次對每一個新生成的值應用函數

數據收集

收集器(Collector)

java.util.stream.Collectors

靜態導入其全部方法

  • counting : 統計

  • maxBy : 最大

  • minBy : 最小

  • summingInt : 求和爲 int

  • summingLong : 求和爲 long

  • summingDouble : 求和爲 double

  • averagingInt : 平均值爲 int

  • averagingLong : 平均值爲 long

  • averagingDouble : 平均值爲 double

  • joining : 聚合

  • toList

  • toSet

  • toCollection

  • toMap

  • toConcurrentMap

分組 (group)

  • groupingBy
  • groupingByConcurrent : 返回的 Map 爲 ConcurrentHashMap
  • collectingAndThen : 收集器結果轉換

示例:

@NoArgsConstructor
@AllArgsConstructor
@Data
public class Cat {
  private String name;
  private Integer age;
}
// 數據初始化
static List<Cat> cats = Arrays.asList(
  new Cat("花貓", 5),
  new Cat("花貓", 6),
  new Cat("花貓", 10),
  new Cat("花貓", 10),
  new Cat("野貓", 5),
  new Cat("野貓", 7),
  new Cat("野貓", 7),
  new Cat("野貓", 7),
  new Cat("野貓", 9)
);
複製代碼
  1. 將 "花貓" 和 "野貓" 進行分類

    Map<String, List<Cat>> collect = cats.stream()
      .collect(groupingBy(Cat::getName));
    System.out.println(collect);
    複製代碼
  2. 統計出 "花貓" 和 "野貓" 的數量

    Map<String, Long> collect = cats.stream()
      .collect(groupingBy(Cat::getName, counting()));
    System.out.println(collect);
    複製代碼
  3. 將 "花貓" 和 "野貓" 進行分類,而後每一個分類中,再按照年齡進行分類

    Map<String, Map<Integer, List<Cat>>> collect = cats.stream()
                    .collect(groupingBy(Cat::getName, groupingBy(Cat::getAge)));
    System.out.println(collect);
    // 若是要多級分類,能夠繼續進行嵌套
    複製代碼
  4. 將 "花貓" 和 "野貓" 進行分類,而後每一個分類中,再按照年齡進行分類,統計出數量

    Map<String, Map<Integer, Long>> collect = cats.stream()
                    .collect(groupingBy(Cat::getName, groupingBy(Cat::getAge, counting())));
            System.out.println(collect);
    複製代碼

分區

  • partitioningBy

Collector 接口

Collector 接口提供本身的實現

// 結果容器
Supplier<A> supplier();

// 計算
BiConsumer<A, T> accumulator();

// 對結果進行合併
BinaryOperator<A> combiner();

// 最終轉換
Function<A, R> finisher();

// 返回一個 Characteristics 集合,定義了收集器的行爲
Set<Characteristics> characteristics();
複製代碼

Characteristics

  • CONCURRENT

    accumulator 函數能夠從多個線程同時調用,且該收集器能夠並行歸約流。

  • UNORDERED

    歸約結果不受流中項目的遍歷和累積順序的影響

  • IDENTITY_FINISH

    方法返回的函數是一個恆等函數,能夠跳過。這種狀況下,累加器對象將會直接用做歸約過程的最終結果

public class MyCollector<T> implements Collector<T, List<T>, List<T>> {

    @Override
    public Supplier<List<T>> supplier() {
        return ArrayList::new;
    }

    @Override
    public BiConsumer<List<T>, T> accumulator() {
        return List::add;
    }

    @Override
    public BinaryOperator<List<T>> combiner() {
        return (list1, list2) -> {
            list1.addAll(list2);
            return list1;
        };
    }

    @Override
    public Function<List<T>, List<T>> finisher() {
        return Function.identity();
    }

    @Override
    public Set<Characteristics> characteristics() {
        return Collections.unmodifiableSet(EnumSet.of(
                IDENTITY_FINISH,
                CONCURRENT
        ));
    }
}
複製代碼

並行處理

並行流

parallel()

parallelStream()

日誌調試

peek

Optional

建立 Optional 對象

  • 建立一個空的 optional 對象

    Optional<String> empty = Optional.empty();
    複製代碼
  • 依據一個非空值建立 Optional

    Optional<String> obj = Optional.of("this is Optional Object");
    複製代碼
  • 可接受 null 的 Optional

    String str = null;
    Optional<String> optionalStr = Optional.ofNullable(str);
    複製代碼

使用 map 從 Optional 對象中提取和轉換值

String str = null;
Optional<String> optionalStr = Optional.ofNullable(str);
Optional<Integer> integer = optionalStr.map(String::length);
Integer integer1 = integer.get();
System.out.println(integer1);
複製代碼

使用 flatMap 連接 Optional 對象

使用 map 操做後,若是返回的對象自己是 Optional 包裝的,那麼就會組成 Option<Option<?>> ,須要使用 flatMap 打散。

Optional 對象的默認行爲

  • get()
  • orElse(T other)
  • orElseGet(Supplier<? extends T)
  • orElseThrow(Supplier<? extends X> exceptionSupplier)
  • ifPresent(Consumer<? super T)
方法 描述
empty 返回一個空的 Optional 實例
filter 若是值存在而且知足提供的謂詞,就返回飲食該值的 Optional 對象,不然返回一個空的 Optional 對象
flatMap 若是人才濟濟存在,就對該值提供的 mapping 函數調用,返回一個 Optional 類型的值,不然就返回一個空的 Optional 對象
get 若是該值存在,將該值用 Optional 封裝返回,不然拋出一個 NoSuchElementException 異常
ifPresent 若是值存在,就執行使用該值的諒調用,不然什麼也不作
map 若是值存在,就對該值執行提供的 mapping 函數調用
of 將指定值用 Optional 封裝以後返回,若是該值爲 null,則拋出一個 NullPointerCeption 異常
ofNullable 將指定什表 Optional 封裝以後返回,若是該值爲 null,則返回一個空的 Optional 對象
orElse 若是有值則將其返回,不然返回一個默認值
orElseGet 若是有值則將返回,不然返回一個由指定的 Supplier 接口生成的值
orElseThrow 若是有值則將其返回,不然拋出一個由指定的 Supplier 接口生成的異常

CompletableFuture

建立 CompletableFuture 對象

public Future<Double> getPriceAsync(String product) {
  CompletableFuture<Double> futurePrice = new CompletableFuture<>();
  new Thread(() -> {                  //建立CompletableFuture對象,它會包含計算的結果
    double price = calculatePrice(product);    //在另外一個線程中以異步方式執行計算
    futurePrice.complete(price); //需長時間計算的任務結束並得出結果時,設置Future的返回值
  }).start();
  return futurePrice;    //←─無需等待還沒結束的計算,直接返回Future對象
}
複製代碼

異常處理,使用 completeExceptionally 方法將異常從線程中傳遞出來

public Future<Double> getPriceAsync(String product) {
  CompletableFuture<Double> futurePrice = new CompletableFuture<>();
  new Thread( () -> {                  //←─建立CompletableFuture對象,它會包含計算的結果
    try {
      double price = calculatePrice(product);    //←─在另外一個線程中以異步方式執行計算
      futurePrice.complete(price);    //←─需長時間計算的任務結束並得出結果時,設置Future的返回值
    } catch (Exception e) {
      futurePrice.completeExceptionally(e);
    }
  }).start();
  return futurePrice;    //←─無需等待還沒結束的計算,直接返回Future對象
}
複製代碼

使用內置的靜態方法(工廠方法)

public Future<Double> getPriceAsync1(String product) {
  return CompletableFuture.supplyAsync(() -> calculatePrice(product));
}
複製代碼

整合兩個 CompletableFuture

CompletableFuture<Double> futurePriceInUSD = 
  CompletableFuture.supplyAsync(() -> shop.getPrice(product))
      .thenCombine(CompletableFuture.supplyAsync(
    	    () ->  ExchangeService.getRate(Money.EUR, Money.USD)),
  		    (price, rate) -> price * rate
	);
複製代碼
  • thenCombine
  • thenCombineAsync

新的時間和日期 API

LocalDate、LocalTime

使用 of 方法建立實例,靜態不可變對象

LocalDateTime

合併了 LocalDate 和 LocalTime

Instant 時間戳

Duration 、 Period

方法名 是否靜態方法 方法描述
between 建立兩個時間點之間的 interval
from 由一個臨時時間點建立interval
of 由它的組成部分建立 interval 的實例
parse 由字符串建立 interval 的實例
addTo 建立該 interval 的副本,並將其疊加到某個指定的 temporal 對象
get 讀取該 interval 的狀態
isNegative 檢查該 interval 是否爲負值,不包含零
isZero 檢查該 interval 的時長是否爲零
miuns 經過減去必定的時間建立該 interval 的副
multipliedBy 將 interval 的值乘以某個標量建立該 interval 的副本
negated 以忽略某個時長的方式去建立該 interval 的副本
plus 以增長某個指定的時長的方式建立該 interval 的副本
subtractFrom 從指定的 termporal 對象中減去該 interval

操縱、解析和格式化日期

方法名 是不是靜態方法 描述
from 依據傳入的 Temporal 對象建立對象實例
now 依據系統時鐘建立 Temporal 對象
of 由 Temporal 對象的某個部分建立該對象的實例
parse 由字符串建立 Temporal 對象的實例
atOffset 由字符串建立 Temporal 對象的實例
atZone 將 Temporal 對象和某個時區相結合
format 使用某個指定的格式器,將 Temporal 對象轉換成爲字符串
get 讀取 Temporal 對象的某一部分的值
minus 建立 Temporal 對象的一個副本,經過將當前 Temporal 對象的值減去必定的時長建立該副本
plus 建立 Temporal 對象的一個副本,經過將當前 Temporal 對象的值加上必定的時長建立該副本
with 以該 Temporal 對象爲模板,對某些狀態進行修改建立該對象的副本

TemporalAdjuster

進行更加複雜的操做,能夠使用重載版本的 with 方法傳遞一個更多定製化的 TemporalAdjuster 對象。

方法名 描述
dayOfWeekInmonth 建立一個新的日期,它的值爲同一個月中每一週的第幾天
firstDayOfMonth 建立一個新的日期,它的值爲當月的第一天
firstDayOfNextMonth 建立一個新的日期,它的值爲下月的第一天
firstDayOfNextYear 建立一個新的日期,它的值爲明年的第一天
firstDayOfYear 建立一個新的日期,它的值爲當年的第一天
firstInMonth 建立一個新的日期,它的值爲同一個月中,第一個符合星期幾要求的值
lastDayOfMonth 建立一個新的日期,它的值爲下月的最後一天
lastDayOfNextMonth 建立一個新的日期,它的值爲下月的最後一天
lastDayofNextYear 建立一個新的日期,它的值爲明年的最後一天
lastDayOfYear 建立一個新的日期,它的值爲今年的最後一天
lastInMonth 建立一個新的日期,它的值爲同一個月中,最後一個符合星期幾要求的值
next/previous 建立一個新的日期,並將其設定爲日期調整後或者調整前,前一個符合指定星期幾要求的日期
nextOrSame/previousOrSame 建立一個新的日期,並將其值設定爲日期調整後或者調整前,第一個符合指定星期幾要求的日期,若是該日期已經符合要求,直接返回該對象

以上 JDK 提供的仍然沒法知足要求,能夠建立本身的 TemporalAdjuster

@FunctionalInterface
public interface TemporalAdjuster {
    Temporal adjustInto(Temporal temporal);
}
複製代碼

實現 TemporalAdjuster 接口,而後在 adjustInto 方法中實現本身的邏輯。

自定義的 TemporalAdjuster 實現

public void testTemporalAdjuster() {
  LocalDateTime now = LocalDateTime.now();
  LocalDateTime nextYear = now.with((t) -> t.plus(1, ChronoUnit.YEARS));

  String format = now.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
  System.out.println(format);

  String format1 = nextYear.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
  System.err.println(format1);
}
複製代碼

功能:獲取下一年的日期時間對象。

DateTimeFormatter

  • 靜態常量,預約義的格式
  • ofPattern 指定格式
相關文章
相關標籤/搜索