Java8用法总结

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

Java8⽤法总结
⼀、新特性
Java8带来了很多的新特性,本篇就以下⼏个⽅⾯,从实际⽤法的⾓度进⾏介绍。

Lambda 表达式
函数式接⼝
Stream
默认⽅法
Optional 类
⼆、Lambda表达式
2.1 引例
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Product {
private String id;
private Long num;
private Double price;
}
为了以后排序,我们定义⼀种⽐较器,按价格排序:
Comparator<Product> byPrice = new Comparator<Product>() {
@Override
public int compare(Product o1, Product o2) {
return o1.getPrice().compareTo(o2.getPrice());
}
};
byPrice的作⽤是按价格⽐较2种产品,它是⼀种⾏为,可以⽤Lambda表达:
Comparator<Product> byPrice = (Product o1, Product o2) -> o1.getPrice().compareTo(o2.getPrice();
这⾥只有⼀种类型Product,可根据Comparator<Product>判断,因此进⼀步简化:
Comparator<Product> byPrice = (o1, o2) -> o1.getPrice().compareTo(o2.getPrice();
2.2 概念
Lambda表⽰⼀种⾏为,通过Lambda表达式将⾏为参数化,这样,⾏为可以和对象⼀样传递;从第三章可以了解,Lambda表达式可以⽤函数式接⼝表⽰,Comparator就是⼀种函数式接⼝;
2.3 表达式
Lambda表达式有三部分,参数列表、"->"、Lambda主体,实际中有以下2种形式:
(parameters) -> expression
(parameters) ->{ statements; }
(List<Product> list) -> list.isEmpty; // 判断队列为空
() -> new Product(); // 新建⼀个对象
(String s) -> s.length; // 求字符串长度
(Product p) -> System.out.println(p); // 输出对象
三、函数式接⼝
3.1 相关概念
函数式接⼝:只定义⼀个抽象⽅法的接⼝;它可能还会有很多默认⽅法,但有且仅有⼀个抽象⽅法;常见的函数式接⼝如Comparator, Runnable;函数式接⼝可以⽤来
表达Lamdba表达式;如将⼀个Lamdba表达式传递给⼀个函数式接⼝,即Lamdba表达式以内联的⽅式实现了函数式接⼝;
函数描述符:函数式接⼝的抽象⽅法;
如果我们想写⼀个⽤于2个数计算的计算器,可能需要实现如下⼏个函数,根据运算符调⽤对应函数计算;
public <T> T add(T a, T b);
public <T> T add(T a, T b);
public <T> T multiply(T a, T b);
public <T> T divide(T a, T b);
换⼀种思路,如果有这样⼀个函数 public double func(double a, double b, Function f); f是⼀个函数式接⼝,它表⽰具体运算,具体代码实现如下:
@Log4j2
public class T19 {
public static void main(String[] args) {
(myFunction(1, 2, (a, b) -> a + b));
(myFunction(1.0, 2.0, (a, b) -> a - b));
(myFunction(BigDecimal.ZERO, BigDecimal.valueOf(2), (a, b) -> a.multiply(b)));
}
public static <T> T myFunction(T a, T b, MyBiFunctionInterface<T> f) {
return f.apply(a, b);
}
}
@FunctionalInterface
public interface MyBiFunctionInterface<T> {
T apply(T a, T b);
}
输出如下:
2018-09-01 19:39:11 - test.other.T19 INFO test.other.T19.main(T19.java:20) : 3
2018-09-01 19:39:11 - test.other.T19 INFO test.other.T19.main(T19.java:21) : -1.0
2018-09-01 19:39:11 - test.other.T19 INFO test.other.T19.main(T19.java:22) : 0
Java8提供了很多函数式接⼝,⼀般情况下不⽤去定义函数式接⼝,⽐如例⼦中MyBiFunctionInterface,可⽤BinaryOperator代替,BinaryOperator这个函数式接⼝,接收2个类型为T的参数,返回⼀个类型为T的结果,即(T, T) -> T,修改后如下:
public static <T> T myFunction(T a, T b, BinaryOperator<T> f) {
return f.apply(a, b);
}
3.3 常见函数式接⼝
Function<T, R>T-> R
Predict<T>T -> boolean
Consumer<T>T -> void
Supplier<T>() -> T
UnaryOperator<T>T -> T
BinaryOperator<T>(T, T) -> T
BiFunction<T, U>(T, U) -> R
BiPredicate<L, R>(L, R) -> boolean
BiConsumer<T, U>(T, U) -> void
3.4 ⽅法引⽤
Lamdba表达式的快捷写法,它更直观,可读性更好,⽐如:(Product p) -> p.getPrice == Product::getPrice
⽅法引⽤主要有⼆类:
(1)指向静态⽅法;如 Integer::parseInt;
(2)指向实例⽅法:如 String::length;
(3)构造函数的⽅法引⽤:如Supplier<Product> p = Product::new;
例:第⼆章引例中还可以如下表达:
Comparator<Product> c = paring(Product::getPrice);
复合,就是将多个Lamdba表达式连接起来,表⽰更加复杂的功能;主要有以下三种
(1)函数复合:将Function代表的Lamdba复合起来,有andThen, compose;其中
f.andThen(g) = g(f(x)),先计算f表达式,将结果再计算g表达式;
pose(g) = f(g(x)),先计算g表达式,将结果再计算f表达式;
Function<Integer, Integer> f = x -> x + 1;
Function<Integer, Integer> g = x -> x * 2;
Function<Integer, Integer> h1 = f.andThen(g); // (1 + 1) * 2 = 4
Function<Integer, Integer> h2 = pose(g); // (1 * 2) + 1 = 3
(2)Predicate的复合,有negate, and, or,分别表⽰⾮、且、或,按从左到右的顺序
Predicate<Product> p1 = a -> a.getPrice() > 100; // ⼤于100
Predicate<Product> p2 = p1.negate(); // ⼩于等于100
Predicate<Product> p3 = p1.negate().and(a -> a.getNum() > 10); // 价格⼩于等于100,且数量⼤于10
(3)⽐较器复合,如
Comparator<Product> c = paring(Product::getPrice).reversed().thenComparing(Product::getNum);
四、流
4.1 概念
流⽤来处理数据集合,它具有如下特点:
(1)流强调的是计算,它是源+数据处理,流将外部迭代(如for/while)转化为对我们透明的内部迭代;
(2)只能遍历⼀次,遍历完就关闭;
流具有如下优点:
(1)内置了很多常⽤⽅法(如排序、分类、统计);
(2)能透明的并⾏处理;
(3)声明式的,只需关注我要怎么样,不⽤关注我该如何实现,通过内置的⽅法与复合很容易实现;
4.2 流的操作
流的操作分为:
(1)中间操作:filter(Predicate<T>), map(Function(T, R), limit, sorted(Comparator<T>), distinct,flatMap;
(2)终端操作:只有终端操作才能产⽣输出,包括:allMatch, anyMatch, noneMatch, findAny, findFirst, forEach, collect, reduce, count
4.3 流的⽤法
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Product {
private String id;
private Long num;
private Double price;
private Boolean isUse;
}
List<Product> list = Lists.newArrayList(
Product.builder().id("11").num(20l).price(100d).isUse(true).build(),
Product.builder().id("12").num(25L).price(120d).isUse(true).build(),
Product.builder().id("13").num(25L).price(100d).isUse(true).build(),
Product.builder().id("14").num(20L).price(110d).isUse(false).build()
);
(1)filter, 找出价格⼤于100的产品:
List<Product> list1 = list.stream().filter(p -> p.getPrice() > 100).collect(Collectors.toList());
(2)distinct,去重
Arrays.asList(1, 2, 3, 1).stream().distinct().forEach(System.out::print); // 输出123
(3)limit,输出前n个
Arrays.asList(1, 2, 3, 1).stream().limit(2).forEach(System.out::print); //输出12
(4)skip,跳过前n个
Arrays.asList(1, 2, 3, 1).stream().skip(2).forEach(System.out::print); // 输出31
(5)map, 映射,T -> R
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
list.stream().map(Product::getPrice).distinct().forEach(System.out::println);
输出:
100.0
120.0
110.0
(6)flatMap,扁平化,将每个元素产⽣的中间集合合并成⼀个⼤集合;接收的Function将T->Stream<R> <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
Arrays.asList(new String[]{"hello", "world"}).stream().map(p -> p.split(""))
.flatMap(Arrays::stream) //.flatMap(p -> Arrays.stream(p))
.distinct().forEach(System.out::print);
// 输出:helowrd
(7)匹配
boolean anyMatch(Predicate<? super T> predicate);
allMatch: 都满⾜条件才返回true;
anyMatch: 有⼀个满⾜就返回true;
noneMatch: 都不满⾜才返回true;
boolean b = Arrays.asList(1, 2, 3, 1).stream().anyMatch(p -> p > 2); //返回true
(8)查找,与其它操作结合使⽤
findAny: Optional<T> findAny()
findFirst: Optional<T> findFirst()
Arrays.asList(1, 2, 3, 4, 1).stream().filter(p -> p > 2).findAny() //输出Optional[3]
Arrays.asList(1, 2, 3, 4, 1).stream().filter(p -> p > 2).findFirst() //输出Optional[3]
4.4 reduce归约
归约操作是很常⽤的操作,它将流中的值反复的结合起来,最终得到⼀个值,它是⼀种终端操作;
(1)Optional<T> reduce(BinaryOperator<T> accumulator);
(2)T reduce(T identity, BinaryOperator<T> accumulator);
(3)<U> U reduce(U identity,
BiFunction<U, ? super T, U> accumulator,
BinaryOperator<U> combiner);
(1)给定归约算法,最终归约成⼀个值,考虑到流可能为空,所以返回类型为Option,例:
Optional<Integer> op1 = Arrays.asList(1, 2, 3, 4, 1).stream().reduce(Integer::sum); //输出Optional[11]
(2)给定了初值,归约算法,返回结果;
Arrays.asList(1, 2, 3, 4, 1).stream().reduce(0, Integer::sum); //输出11
// Steam<T>中T为包装类型,没有sum,但Java8为流的原始类型提供了⼀些⽅法,如下
Arrays.asList(1, 2, 3, 4, 1).stream().mapToInt(a -> a).sum();
list.stream().mapToLong(Product::getNum).sum();
(3)第三个参数表⽰合并⽅式,当是并⾏流时,各线程独⽴计算结果,最后将各线程的结果合并;BiFunction<Double, Product, Double> f1 = (Double a, Product b) -> a + b.getNum();
BinaryOperator<Double> f2 = (a, b) -> a + b;
double b2 = list.parallelStream().reduce(0d, f1, f2);
(b2); //输出90
4.5 数值流
数值流除了具有流的⽅法外,还有⼀些特殊的统计⽅法,例
DoubleStream doubleStream = list.stream().mapToDouble(Product::getPrice);
double average = doubleStream.average().getAsDouble();
//数值流->对象流
Stream<Double> sd = doubleStream.boxed();
// ⽣成n以内的勾股数
Stream<double[]> stream = IntStream.rangeClosed(1, 30).boxed().flatMap(a -> IntStream.rangeClosed(a, 30).mapToObj(
b -> new double[]{a, b, Math.sqrt(a * a + b * b)}).filter(t -> t[2] % 1 == 0));
stream.limit(3).forEach(t -> System.out.println(t[0] + ", " + t[1] + ", " + t[2]));
输出:
3.0,
4.0,
5.0
5.0, 12.0, 13.0
6.0, 8.0, 10.0
4.6 构建流
Stream.iterate(0, n -> n + 2).limit(10);
Stream.generate(Math::random).limit(10);
五、收集器(collect归约)
5.1 常见⽤法
Map<Double, List<Product>> map = list.stream().collect(groupingBy(Product::getPrice));
Long allNum = list.stream().collect(summingLong(Product::getNum));
double average = list.stream().collect(averagingDouble(Product::getPrice));
LongSummaryStatistics statistics = list.stream().collect(summarizingLong(Product::getNum));
String ids = list.stream().map(Product::getId).collect(joining(", "));
5.2 reducing归约
Optional<Product> opp = list.stream().collect(reducing((a, b) -> a.getPrice() > b.getPrice() ? a : b));
long allNum2 = list.stream().collect(reducing(0L, Product::getNum, Long::sum));
long allNum3 = list.stream().collect(reducing(0L, Product::getNum, (i, j) -> i + j));
collect中reducing归约三要素,初值,提取值,归约⽅法,若⽆初值返回Optional,若提取值即是对象本⾝,可省略;
5.3 多重分组
Map<Double, Map<Long, List<Product>>> map = list.stream().collect(groupingBy(Product::getPrice, groupingBy(Product::getNum))); Map<Double, Map<String, List<Product>>> map2 = list.stream().collect(groupingBy(Product::getPrice, groupingBy(p -> {
if (p.getNum() <= 80L)
return "little";
else if (p.getNum() >= 120L)
return "many";
else
return "normal";
})));
System.out.println(JacksonUtil.toJson(map));
System.out.println(JacksonUtil.toJson(map2));
输出如下:
{
"100.0" : {
"20" : [ {
"id" : "11",
"num" : 20,
"price" : 100.0,
"isUse" : true
} ],
"25" : [ {
"id" : "13",
"num" : 25,
"price" : 100.0,
"isUse" : true
} ]
},
"110.0" : {
"20" : [ {
"id" : "14",
"num" : 20,
"price" : 110.0,
"isUse" : false
} ]
},
"120.0" : {
"25" : [ {
"id" : "12",
"num" : 25,
"price" : 120.0,
"isUse" : true
} ]
}
}
{
"100.0" : {
"little" : [ {
"id" : "11",
"num" : 20,
"price" : 100.0,
"isUse" : true
}, {
"id" : "13",
"num" : 25,
"price" : 100.0,
"isUse" : true
} ]
},
"110.0" : {
"little" : [ {
"id" : "14",
"num" : 20,
"price" : 110.0,
"isUse" : false
} ]
},
"120.0" : {
"little" : [ {
"id" : "12",
"num" : 25,
"price" : 120.0,
"isUse" : true
} ]
}
}
在⼀次分组的⼦集合中处理数据
Map<Double, Long> map = list.stream().collect(groupingBy(Product::getPrice, counting()));
Map<Double, Optional<Product>> map2 = list.stream().collect(groupingBy(Product::getPrice, maxBy(comparingLong(Product::getNum)))); Comparator<Product> c = ((p1, p2) -> p1.getNum().compareTo(p2.getNum()));
Map<Double, Optional<Product>> map3 = list.stream().collect(groupingBy(Product::getPrice, maxBy(c)));
Map<Double, Product> map4 = list.stream().collect(groupingBy(Product::getPrice,
collectingAndThen(maxBy(comparing(Product::getNum)), Optional::get)));
5.4 分区
由⼀个谓词作为分类,分为2类,true与false,⽤法与groupingBy完全⼀样
Map<Boolean, List<Product>> map = list.stream().collect(partitioningBy(Product::getIsUse));
Map<Boolean, Map<Double, List<Product>>> map2 = list.stream().collect(partitioningBy(Product::getIsUse,
groupingBy(Product::getPrice)));
Map<Boolean, LongSummaryStatistics> map3 = list.stream().collect(partitioningBy(Product::getIsUse,
summarizingLong(Product::getNum)));
Map<Boolean, Double> map4 = list.stream().collect(partitioningBy(Product::getIsUse, averagingLong(Product::getNum)));
六、optional
6.1 使⽤
(1)单级包装⽤法:我们会见到如下代码,
String name = null;
if (product != null) {
name = product.getId();
}
利⽤optional可转化为
Optional<Product> optProduct = Optional.ofNullable(product);
Optional<String> optName = optProduct.map(Product::getId);
(2)多级包装⽤法
public String getName(Person person) {
return person.getCar().getInsurance().getName();
}
经过包装如下,注意为防⽌Optional<Optional<T>>这种中间结果造成编译不通过,需要使⽤flatMap public String getName(Person person) {
Optional<Person> optPerson = Optional.ofNullable(person);
return optPerson.flatMap(Person::getCar).flatMap(Car::getInsurance).map(Insurance::getName).orElse("Unknown"); }。

相关文档
最新文档