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); } } 复制代码
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); }; } } 复制代码
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; } } 复制代码
java.util.function.Supplier
@FunctionalInterface public interface Supplier<T> { // 函数式接口,返回 T,无参数 T get(); } 复制代码
同时为简单的数据类型准备了对应的函数式接口,一般是在 Predicate
加上对应的前缀,比如 double
对应的 Predicate
接口为 DoublePredicate
,
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
compose
和 andThen
都返回 Function
对象,可以将其进行复合。
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 : 查找任意一个
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(); } 复制代码
Stream.iterate(0, n -> n + 2) .limit(10) .forEach(System.out::println); 复制代码
Stream.generate(Math::random) .limit(5) .forEach(System.out::println); 复制代码
iterate 与 generate 的区别:
iterate 方法接受一个初始值,依次应用在每个产生新值上的 Lambda
generate 不是依次对每个新生成的值应用函数
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
示例:
@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) ); 复制代码
将 "花猫" 和 "野猫" 进行分类
Map<String, List<Cat>> collect = cats.stream() .collect(groupingBy(Cat::getName)); System.out.println(collect); 复制代码
统计出 "花猫" 和 "野猫" 的数量
Map<String, Long> collect = cats.stream() .collect(groupingBy(Cat::getName, counting())); System.out.println(collect); 复制代码
将 "花猫" 和 "野猫" 进行分类,然后每个分类中,再按照年龄进行分类
Map<String, Map<Integer, List<Cat>>> collect = cats.stream() .collect(groupingBy(Cat::getName, groupingBy(Cat::getAge))); System.out.println(collect); // 如果要多级分类,可以继续进行嵌套 复制代码
将 "花猫" 和 "野猫" 进行分类,然后每个分类中,再按照年龄进行分类,统计出数量
Map<String, Map<Integer, Long>> collect = cats.stream() .collect(groupingBy(Cat::getName, groupingBy(Cat::getAge, counting()))); System.out.println(collect); 复制代码
为 Collector
接口提供自己的实现
// 结果容器 Supplier<A> supplier(); // 计算 BiConsumer<A, T> accumulator(); // 对结果进行合并 BinaryOperator<A> combiner(); // 最终转换 Function<A, R> finisher(); // 返回一个 Characteristics 集合,定义了收集器的行为 Set<Characteristics> characteristics(); 复制代码
Characteristics
accumulator 函数可以从多个线程同时调用,且该收集器可以并行归约流。
归约结果不受流中项目的遍历和累积顺序的影响
方法返回的函数是一个恒等函数,可以跳过。这种情况下,累加器对象将会直接用作归约过程的最终结果
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<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); 复制代码
String str = null; Optional<String> optionalStr = Optional.ofNullable(str); Optional<Integer> integer = optionalStr.map(String::length); Integer integer1 = integer.get(); System.out.println(integer1); 复制代码
使用 map 操作后,如果返回的对象本身是 Optional 包装的,那么就会组成 Option<Option<?>> ,需要使用 flatMap 打散。
方法 | 描述 |
---|---|
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 接口生成的异常 |
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<Double> futurePriceInUSD = CompletableFuture.supplyAsync(() -> shop.getPrice(product)) .thenCombine(CompletableFuture.supplyAsync( () -> ExchangeService.getRate(Money.EUR, Money.USD)), (price, rate) -> price * rate ); 复制代码
使用 of
方法创建实例,静态不可变对象
合并了 LocalDate 和 LocalTime
方法名 | 是否静态方法 | 方法描述 |
---|---|---|
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 对象为模板,对某些状态进行修改创建该对象的副本 |
进行更加复杂的操作,可以使用重载版本的 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 方法中实现自己的逻辑。
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); } 复制代码
功能:获取下一年的日期时间对象。