Java 8 的一些新特性

函数式接口

Predicate

java.util.function.Predicate

@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.Function

@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.Supplier

@FunctionalInterface
public interface Supplier<T> {
    // 函数式接口,返回 T,无参数
    T get();
}
复制代码

同时为简单的数据类型准备了对应的函数式接口,一般是在 Predicate 加上对应的前缀,比如 double 对应的 Predicate 接口为 DoublePredicate

复合 Lambda 表达式

比较器

Compartor<Person> c = Comparator.comparing(Person::getName);
复制代码

逆序

List<Apple> inventory = new ArrayList<>();
inventory.sort(comparing(Apple::getWeight).reversed());
复制代码

比较器链

thenComparing ,如果对象的第一个 Compartor 比较之后是一样的,就使用第二个 Compartor

List<Apple> inventory = new ArrayList<>();
inventory.sort(comparing(Apple::getWeight).reversed().thenComparing(Apple::getCountry));
复制代码

谓词复合

  • negate

    表示非

  • 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 指定格式

猜你喜欢

转载自juejin.im/post/5cde1800f265da035f6fffab