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) {
Price()Price());
}
};
byPrice的作⽤是按价格⽐较2种产品,它是⼀种⾏为,可以⽤Lambda表达:
Comparator<Product> byPrice = (Product o1, Product o2) -> o1.getPrice()Price();
这⾥只有⼀种类型Product,可根据Comparator<Product>判断,因此进⼀步简化:
Comparator<Product> byPrice = (o1, o2) -> o1.getPrice()Price();
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) {
log.info(myFunction(1, 2, (a, b) -> a + b));
log.info(myFunction(1.0, 2.0, (a, b) -> a - b));
log.info(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 - her.T19 her.T19.main(T19.java:20) : 3
2018-09-01 19:39:11 - her.T19 her.T19.main(T19.java:21) : -1.0
2018-09-01 19:39:11 - her.T19 her.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 = Comparatorparing(Product::getPrice);
复合,就是将多个Lamdba表达式连接起来,表⽰更加复杂的功能;主要有以下三种
(1)函数复合:将Function代表的Lamdba复合起来,有andThen, compose;其中
f.andThen(g) = g(f(x)),先计算f表达式,将结果再计算g表达式;
fpose(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 = fpose(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 = Comparatorparing(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 = wArrayList(
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).List());
(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);
log.info(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);
五、收集器(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));java中split的用法
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.Json(map));
System.out.Json(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" : [ {
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论