本文最后更新于1 分钟前,文中所描述的信息可能已发生改变。
jdk8中加入了很多新特性主要学习最常用的三点:
- 函数式接口、
- Lambda表达式、
- Optional、
- Stream流、
函数式接口:
概念: 函数式接口就是只包含一个抽象方法的接口。
在jdk8中,引入了Predicate
、Function
、Consumer
、Supplier
等接口,这些接口都是函数式接口,因为他们只包含一个抽象方法。
在jdk8中,定义一个函数式接口都会带有@FunctionalInterface
注解,表示这是一个函数式接口。 常用的函数式接口以及其使用场景如下:
常用的函数式接口:
- Predicate(断言): 接受一个参数,返回布尔值结果。
Predicate<Integer> predicate = (num) -> num > 0;
- Function(函数): 接受一个参数,返回一个结果。
Function<Integer, String> function = (num) -> "Number is: " + num;
- Consumer(消费者): 接受一个参数,不返回结果,常用于处理数据。
Consumer<String> consumer = (str) -> System.out.println(str);
- Supplier: 一般用于需要延时加载或获取某个数据时使用。 示例代码:
Supplier<Double> randomSupplier = () -> Math.random();
double randomValue = randomSupplier.get();
Supplier<Connection> connectionSupplier = () -> DriverManager.getConnection("jdbc:mysql://localhost:3306/mydatabase", "username", "password");
Connection connection = connectionSupplier.get();
Lambda表达式:
jdk8的函数式接口
我觉得函数式接口主要是为了更好的实现lambda下的函数式编程。可以将一些功能很好的与lambda表达式更好的结合。
实例代码: 先声明一个函数式接口 OrderService
, 包含一个generateOrder
方法
@FunctionalInterface
public interface OrderService<K> {
String generateOrder( K orderId);
}
普通代码写法:
OrderService IntegerCode = new OrderService<Integer>() {
@Override
public String generateOrder(Integer orderId) {
return orderId + "_code";
}
};
Lambda方式写法:
OrderService<Integer> intOrderCode = orderId -> orderId + "_code";
可以发现通过lambda表达式的方式,更优雅的书写上面的代码.更符合函数式编程的写法。
Stream流:
1.创建流:
集合:集合对象.stream() 数组:Arrays.stream( 数组) 双列集合:转换成单列集合后再创建
Map<String,Integer> map = new HashMap<>();
map.put("1",1);
map.put("2",2);
map.put("3",3);
Stream<Map.Entry<String, Integer>> stream = map.entrySet().stream();
2.操作流:
filter 过滤
Map<String,Integer> map = new HashMap<>();
map.put("1",1);
map.put("2",2);
map.put("3",3);
map.entrySet().stream().filter(x -> x.getValue() > 1)
map
可以将流中的对象进行计算或转换。
Map<String,Integer> map = new HashMap<>();
map.put("1",1);
map.put("2",2);
map.put("3",3);
map.entrySet().stream()
.filter(x -> x.getValue() > 1)
.map(x -> x.getKey()) //获取过滤后的key值
distinct
去重,依赖的是Object的equals 方法来判断是否是相同的对象。 需要重写equals方法。
sorted
可以对流元素排序
Map<String,Integer> map = new HashMap<>();
map.put("1",1);
map.put("2",2);
map.put("3",3);
map.entrySet().stream()
.filter(x -> x.getValue() > 1)
.map(x -> x.getKey())
.sorted(String::compareTo)
limit
可以设置流的最大长度,超出的部分将抛弃。
flatMap
map 只能将一个对象转换成另一个对象来作为流中的元素。flatMap则可以吧一个对象转换成多个对象作为流中的元素。
authors.stream()
.flatMap(a -> a.getBooks().stream())
.map(b -> b.getName())
.distinct()
.forEach(System.out::println);
flatmap需要保证最后返回的类型为 stream即可
3.终结操作:
foreach
对流中的元素进行遍历操作
count
获取流中元素的个数
collect
将流转换成集合转换成List 集合: .collect(Collectors.toList());
转换成Set 集合: .collect(Collectors.toMap(k->k, v->v+"_value"));
需要传入两个 function
分别定义key
与value
的取值方式
4. 查找与匹配:
匹配
anyMatch
判断是否有符合匹配的元素.anyMatch(s -> s.equals("a")) ? tmp.stream().collect(Collectors.toMap(s -> s, s -> s)) : null;
allMatch
判断是否都满足匹配条件
noneMatch
是否全部不符合条件
查找
findAny
查找任意一个匹配的元素
findFirst
查找第一个匹配的元素
reduce归并
对流中的元素按照既定的规则进行计算,得出一个结果 (缩减操作)其中的操作类似于对集合进行的累加操作
Integer[] arrs = {1, 2, 3, 4, 5};
int sum = 0;
for (Integer i : arrs) {
sum = sum + i;
}
System.out.println(sum);
使用流进行累加操作
Arrays.asList(arrs).stream()
.reduce(0, (a, b) -> a + b);
通过reduce计算最小值
Arrays.asList(arrs).stream()
.reduce(Integer.MIN_VALUE, (res,current) -> res > current ? res : current);
还有一个一个参数的类型的方式:
Arrays.asList(arrs).stream()
.reduce((a,b) -> a > b ? a : b);
第三种方式:
Optional
为了避免代码中总是进行空判断,或者出现空指针异常而出现。创建Optional 对象通常在创建方法时可以直接返回optional的对象创建方式:
Apple apple = new Apple();
Optional<Apple> opApple = Optional.ofNullable(apple);
消费:backservice.getUser().ifPresent(commonResult -> {
System.out.println(commonResult);
});
获取其中的对象:
CommonResult result = backservice.getUser()
.orElseGet(() -> new CommonResult(200, "empty", null));
过滤:
backservice.getUser().filter(r -> r.getCode() == 200)
.ifPresent(r -> System.out.println(r.getData()));
判断:
if (backservice.getUser().isPresent()){
System.out.println(backservice.getUser().orElseGet(() -> new CommonResult(200, "empty", null)).getData());
}
map转换:
backservice.getUser()
.map(r -> r.getData())
.ifPresent(r -> System.out.println(r));
函数式接口说明:概念:只有一个抽象方法的接口称作函数式接口。JDK的函数式接口都添加了 @FunctionalInterface
注解进行了注释。常用的函数式接口:在java/util
包中Consumer
消费接口Function
进行函数计算的接口Predicate
判断类型的接口Supplier
生产类型的接口拼接默认方法
andbackservice.getUser().filter(
((Predicate<CommonResult>) commonResult -> false).and(r -> r.getCode() == 200)
);
ornegate类似于与或非,理解即可
方法引用:
一个函数式的语法糖,可以进一步简洁lambda的写法。规则是如果方法体中只调用了一个方法,或者是构造方法的话,可以使用。规则1 :引用类的静态方法类名::静态方法
String[] strs = {"1", "2", "3", "4", "5"};
Arrays.stream(strs).map(str -> Integer.parseInt(str)).collect(Collectors.toList());
| |
v
Arrays.stream(strs).map(Integer::parseInt).collect(Collectors.toList());
规则2:引用对象实例中的方法对象名::方法
List<String> randoms = Arrays.stream(strs).map(Integer::parseInt).map(this::generate).collect(Collectors.toList());
规则3:构造器引用
对象名::new Arrays.stream(strs).map(StringBuilder::new).map(t->t.append("_kol")).map(StringBuilder::toString).collect(Collectors.toList());
高级用法
书写中的优化注意:
Integer[] ints = {1, 2, 3, 4, 5};
Arrays.stream(ints)
.map(i -> i + 10)
.filter(i -> i > 13)
.collect(Collectors.toList());
当存在上述的书写方式时,Integer 元素会经过多次的拆箱和装箱,当元素的个数达到一定的量的时候会严重影响流的效率。
List<Integer> ss = Arrays.stream(ints)
.mapToInt(i -> i + 10)
.filter(i -> i > 13)
.boxed().collect(Collectors.toList());
可以在开始直接转换为int 类型,防止多次拆装箱,最后通过boxed 装箱为大类。
并行流:
开启并行流关键字parallel()
Stream<Integer> is = Stream.of(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50);
Integer total = is.parallel().reduce(0, (a, b) -> a + b);
System.out.println(total);
peek
通过peek可以进行调试
Integer total = is.parallel()
.peek(i -> System.out.println("thread" + Thread.currentThread().getName() + ": " + i))
.reduce(0, (a, b) -> a + b);
System.out.println(total);
parallelStream()可以直接获取并行流