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 指定格式
相关文章
相关标签/搜索