JAVA8新特性
Lambda表达式
Lambda是一个匿名函数,我们可以把Lambda表达式理解为是一段可以传递的代码(将代码像数据一样传递),使用它可以写出更简洁,更灵活的代码,依赖于函数式接口
(o1, o2)-> Integer.compare(o1,o2);
-> Lambda操作符或者箭头操作符
->左边:Lambda形参列表
->右边:Lambda体(其实就是重写的抽象方法的方法体)
总结
-> 左边:Lambda形参列表的参数类型可以省略(类型推断),如果Lambda形参列表只有一个参数,其一对()也可以省略
->右边:Lambda体应该使用一对{}包裹,如果Lambda体只有一条执行语句(可能是return语句),就可以省略{}以及return关键字
Lambda表达式本质:作为函数式接口的实例
1.无参无返回值
@Test
public void lambdaTest1() {
Runnable runnable = () ->System.out.println("hello world");
runnable.run();
}
2.需要一个参数,但是没有返回值
@Test
public void lambdaTest2(){
Consumer <String>consumer = new Consumer<String>() {
@Override
public void accept(String str) {
System.out.println(str);
}
};
consumer.accept("hello world");
System.out.println(".....................");
Consumer<String> consumer1 = (String str)-> System.out.println(str);
consumer1.accept("world hello");
}
3.数据类型可以省略,因为可由编译器推断得出,称为类型推断
@Test
public void lambdaTest2(){
Consumer <String>consumer = new Consumer<String>() {
@Override
public void accept(String str) {
System.out.println(str);
}
};
consumer.accept("hello world");
System.out.println(".....................");
Consumer<String> consumer1 = (str)-> System.out.println(str);
consumer1.accept("world hello");
}
4. Lambda如果只需要一个参数时,参数的小括号可以省略
@Test
public void lambdaTest2(){
Consumer <String>consumer = new Consumer<String>() {
@Override
public void accept(String str) {
System.out.println(str);
}
};
consumer.accept("hello world");
System.out.println(".....................");
Consumer<String> consumer1 = str-> System.out.println(str);
consumer1.accept("world hello");
}
5. Lambda需要两个货以上的参数,多条执行语句,并且可以有返回值
@Test
public void lambdaTest3() {
Comparator<Integer> comparator = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
}
};
System.out.println(comparator.compare(13,45));
System.out.println("*******************");
Comparator<Integer> comparator1 = (o1,o2)->{
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
};
System.out.println( comparator1.compare(100,100));
}
6.当Lambda体只有一条语句时,return与大括号若有,都可以省略
@Test
public void lambdaTest4(){
Comparator<Integer> comparator1 = (o1,o2)->{
return o1.compareTo(o2);
};
System.out.println(comparator1.compare(15,1));
Comparator<Integer> comparator3 = (o1,o2)-> o1.compareTo(o2);
System.out.println(comparator3.compare(11,12));
}
Functional函数式接口
定义
只包含一个抽象方法的接口,称为函数式接口
可以通过Lambda表达式来创建该接口的对象(若Lambda表达式抛出一个受检异常),那么改异常需要在目标接口的抽象方法上进行声明
可以在接口上使用@FunctionalInterface注解,这样做可以检查它是否是一个函数式接口
如何理解函数式接口
Java不但可以支持OOP也可以支持OOF(面向函数编程)
Lambda表达式的类型是函数,但在Java8中,Lambda表达式是对象不是函数,他们必须依附于一类特别的对象类型->函数式接口
Lambda表达式就是一个函数式接口的实例
所以前面使用匿名实现类表示的现在都可以用Lambda表达式来写
Java内置四大核心函数式接口
| 函数式接口 | 参数类型 | 返回类型 | 用途 |
|---|---|---|---|
| Consumer |
T | void | 对类型为T的对象应用操作,包含方法:void accept(T t) |
| Supplier |
/ | T | 返回类型为T的对象,包含方法 T get() |
| Function<T,R>函数型接口 | T | R | 对类型为T的对象应用操作,并返回结果。结果是R类型的对象,包含方法 R apply(T t) |
| Predicate |
T | boolean | 确定对象为T的对象是否满足某约束,并返回boolean值。包含方法 boo lean test(T t) |
@Test
public void test1(){
happyTime(100.0, new Consumer<Double>() {
@Override
public void accept(Double aDouble) {
System.out.println("消费"+aDouble);
}
});
happyTime(200.0,money-> System.out.println("消费"+money));
}
public void happyTime(Double money,Consumer<Double> consumer){
consumer.accept(money);
}
@Data
@AllArgsConstructor
class User {
private String username;
private Integer age;
}
public List<User> findUser(List<User> userList, Predicate<User> userPredicate) {
ArrayList<User> users = new ArrayList<>();
for (User user : userList) {
if (userPredicate.test(user)) {
users.add(user);
}
}
return users;
}
@Test
public void predicateTest(){
List<User> users = Arrays.asList(new User("zhangsan",18),new User("lisi",17));
List<User> user = findUser(users, new Predicate<User>() {
@Override
public boolean test(User user) {
return user.age >= 18;
}
});
System.out.println(user);
List<User> user1 = findUser(users,userx->userx.age>=18);
System.out.println(user1);
}
方法引用与构造器引用(::关键字)
使用情景:当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用
方法引用可以看作是Lambda表达式深层次的表达,通过方法的名字来指向一个方法
要求:实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的方法的参数和返回值类型保持一致
格式:操作符:: 将类(对象)与方法名分隔开
::关键字提供了四种语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,::关键字可以使语言更简洁,减少冗余代码。
| 语法种类 | 示例 |
|---|---|
| 引用静态方法 | ContainingClass::staticMethodName |
| 引用特定对象的实例方法 | containingObject::instanceMethodName |
| 引用特定类型的任意对象的实例方法 | ContainingType::methodName |
| 引用构造函数 | ClassName::new |
Stream链式操作
Stream操作符大体上分为两种:中间操作符和中止操作符
中间操作:
1. filter(T->boolean)
过滤数据,保留boolean为true的元素,返回一个集合
@Test
public void filterStreamTest() {
List<Integer> integerList = Arrays.asList(10, 20, 5, 100, 50, 43, 47, 66);
List<Integer> collect = integerList.stream().filter(x -> x >= 50).collect(Collectors.toList());
System.out.println(collect);
}
collect(Collectors.toList())可以把流转换为 List 类型,collect实际上是一个终止操作。
2. map(T->R)
转换操作符,可以做数据转换,比如:把字符串转换成int、long、double,或者把一个实体转换成另外一个实体。包含:map,mapToInt、mapToLong、mapToDouble
@Test
public void mapStreamTest() {
List<Integer> integerList = Arrays.asList(10, 20, 5, 100, 50, 43, 47, 66);
integerList.stream().map(key ->String.valueOf(key)).forEach(System.out::println);
}
3. flatMap(T->Stream)
将流中的每一个元素T映射成为流,再把每一个流连接成为一个流
public class FlatMapTest {
@Test
public void testFlatMap() {
List<List<String>> list = new ArrayList<List<String>>(){{
add(Lists.newArrayList("a","b","c"));
add(Lists.newArrayList("d","e","f"));
add(Lists.newArrayList("j","k","y"));
}};
//结果:[[a, b, c], [d, e, f], [j, k, y]]
System.out.println(list);
List<String> collect = list.stream().flatMap(List::stream).collect(Collectors.toList());
//结果:[a, b, c, d, e, f, j, k, y]
System.out.println(collect);
}
}
我们可以看到flatMap可以轻松把字符串的二维数据变成一位数组。
4. distinct
去重,底层使用了equals方法进行比较
@Test
public void testDistinct(){
List<Integer> integerList = Arrays.asList(10, 20, 5, 100, 50, 43, 47, 66,66,20,10,10);
integerList.stream().distinct().forEach(System.out::println);
}
其实,去重还有另外一种办法,可以用Collectors.toSet(),后面会讲到。
5. sorted
对元素进行排序,前提是实现comparable接口,当然也可以自定义比较器
@Test
public void testSort() {
List<Integer> integerList = Arrays.asList(10, 20, 5, 100, 50, 43, 47, 66, 66, 20, 10, 10);
List<Integer> collect = integerList.stream().sorted((a, b) -> a.compareTo(b)).collect(Collectors.toList());
System.out.println(collect);
}
6.limit
限流操作,有点类似于mysql中的limit功能,比如:有10个元素,只取前面3个元素
@Test
public void testLimit() {
List<String> list = Lists.newArrayList("a", "b", "ab", "abc", "a", "ab", "a", "abcd", "bd", "abc");
List<String> collect = list.stream().limit(3).collect(Collectors.toList());
//结果:[a, b, ab]
System.out.println(collect);
}
7. skip
跳过操作,比如:有个10个元素,从第5个元素开始去后面的元素
@Test
public void testSkip() {
List<String> list = Lists.newArrayList("a", "b", "ab", "abc", "a", "ab", "a", "abcd", "bd", "abc");
List<String> collect = list.stream().skip(5).collect(Collectors.toList());
//结果:[ab, a, abcd, bd, abc]
System.out.println(collect);
}
8.peek
挑出操作
@Test
public void testPeek() {
List<String> list = Lists.newArrayList("a", "b", "ab", "abc", "a", "ab", "a", "abcd", "bd", "abc");
//结果:abababcaabaabcdbdabc
list.stream().peek(x -> x.toUpperCase()).forEach(System.out::print);
}
眼尖的朋友会发现,进行x.toUpperCase()转换为大写功能,但是实际上没有生效。把peek改成map方法试试:
@Test
public void testPeek() {
List<String> list = Lists.newArrayList("a", "b", "ab", "abc", "a", "ab", "a", "abcd", "bd", "abc");
//结果:ABABABCAABAABCDBDABC
list.stream().map(x -> x.toUpperCase()).forEach(System.out::print);
}
我们可以看到,用map操作转换成大写功能生效了,但是用peek操作却没有生效。peek只是对Stream中的元素进行某些操作,但是操作之后的数据并不返回到Stream中,所以Stream中的元素还是原来的元素。
终止操作:
1. forEach
遍历操作,包含:forEach 和 forEachOrdered
forEach:支持并行处理
forEachOrdered:是按顺序处理的,遍历速度较慢
@Test
public void testForEach() {
List<String> list = Lists.newArrayList("a", "b", "ab");
//结果:a b ab
list.stream().forEach(x-> System.out.print(x+' '));
System.out.println("");
//可以简化
//结果:a b ab
list.forEach(x-> System.out.print(x+' '));
System.out.println("");
//结果:a b ab
list.stream().forEachOrdered(x-> System.out.print(x+' '));
}
2.collect
收集操作,将所有的元素收集起来,Collectors 提供了非常多收集器。包含:toMap、toSet、toList、joining,groupingBy,maxBy,minBy等操作。
toMap:将数据流转换为map,里面包含的元素是用key/value的形式的
toSet:将数据流转换为set,里面包含的元素不可重复
toList:将数据流转出为list,里面包含的元素是有序的
joining:拼接字符串
groupingBy:分组,可以将list转换map
couting:统计元素数量
maxBy:获取最大元素
minBy:获取最小元素
summarizingInt: 汇总int类型的元素,返回IntSummaryStatistics,再调用具体的方法对元素进行统计:getCount(统计数量),getSum(求和),getMin(获取最小值),getMax(获取最大值),getAverage(获取平均值)
summarizingLong:汇总long类型的元素,用法同summarizingInt
summarizingDouble:汇总double类型的元素,用法同summarizingInt
averagingInt:获取int类型的元素的平均值,返回一个double类型的数据
averagingLong:获取long类型的元素的平均值,用法同averagingInt
averagingDouble:获取double类型的元素的平均值,用法同averagingInt
mapping:获取映射,可以将原始元素的一部分内容作为一个新元素返回
3.find
查找操作,包含:findFirst、findAny
findFirst:找到第一个,返回的类型为Optional
findAny:使用 stream() 时找到的是第一个元素,使用 parallelStream() 并行时找到的是其中一个元素,返回的类型为Optional
4.match
匹配操作,包含:allMatch、anyMatch、noneMatch
allMatch:所有元素都满足条件,返回boolean类型
anyMatch:任意一个元素满足条件,返回boolean类型
noneMatch:所有元素都不满足条件,返回boolean类型
@Test
public void testMatch() {
List<Integer> list = Lists.newArrayList(2, 3, 5, 7);
boolean allMatch = list.stream().allMatch(x -> x > 1);
//结果:true
System.out.println(allMatch);
boolean allMatch2 = list.stream().allMatch(x -> x > 2);
//结果:false
System.out.println(allMatch2);
boolean anyMatch = list.stream().anyMatch(x -> x > 2);
//结果:true
System.out.println(anyMatch);
boolean noneMatch1 = list.stream().noneMatch(x -> x > 5);
//结果:false
System.out.println(noneMatch1);
boolean noneMatch2 = list.stream().noneMatch(x -> x > 7);
//结果:true
System.out.println(noneMatch2);
}
5.count
统计操作,效果跟调用集合的size()方法类似
@Test
public void testCountOp() {
List<String> list = Lists.newArrayList("a", "b", "ab");
long count = list.stream().count();
//结果:3
System.out.println(count);
}
6.min max
min:获取最小值,返回Optional类型的数据
max:获取最大值,返回Optional类型的数据
@Test
public void testMaxMin() {
List<Integer> list = Lists.newArrayList(2, 3, 5, 7);
Optional<Integer> max = list.stream().max((a, b) -> a.compareTo(b));
//结果:7
System.out.println(max.get());
Optional<Integer> min = list.stream().min((a, b) -> a.compareTo(b));
//结果:2
System.out.println(min.get());
}
7.reduce
规约操作,将整个数据流的值规约为一个值,count、min、max底层就是使用reduce。
reduce 操作可以实现从Stream中生成一个值,其生成的值不是随意的,而是根据指定的计算模型。
@Test
public void testReduce() {
List<Integer> list = Lists.newArrayList(2, 3, 5, 7);
Integer sum1 = list.stream().reduce(0, Integer::sum);
//结果:17
System.out.println(sum1);
Optional<Integer> reduce = list.stream().reduce((a, b) -> a + b);
//结果:17
System.out.println(reduce.get());
Integer max = list.stream().reduce(0, Integer::max);
//结果:7
System.out.println(max);
Integer min = list.stream().reduce(0, Integer::min);
//结果:0
System.out.println(min);
Optional<Integer> reduce1 = list.stream().reduce((a, b) -> a > b ? b : a);
//2
System.out.println(reduce1.get());
}
8.toArray
数组操作,将数据流的元素转换成数组。
@Test
public void testArray() {
List<String> list = Lists.newArrayList("a", "b", "ab");
String[] strings = list.stream().toArray(String[]::new);
//结果:a b ab
for (int i = 0; i < strings.length; i++) {
System.out.print(strings[i]+" ");
}
}
stream和parallelStream的区别 stream:是单管道,称其为流,其主要用于集合的逻辑处理。
parallelStream:是多管道,提供了流的并行处理,它是Stream的另一重要特性,其底层使用Fork/Join框架实现
@Test
public void testStream() {
List<Integer> list = Lists.newArrayList(1,2, 3,4, 5,6, 7);
//结果:1234567
list.stream().forEach(System.out::print);
}
@Test
public void testParallelStream() {
List<Integer> list = Lists.newArrayList(1,2, 3,4, 5,6, 7);
//结果:5726134
list.parallelStream().forEach(System.out::print);
}
我们可以看到直接使用parallelStream的forEach遍历数据,是没有顺序的。
如果要让parallelStream遍历时有顺序怎么办呢?
@Test
public void testParallelStream() {
List<Integer> list = Lists.newArrayList(1,2, 3,4, 5,6, 7);
//结果:1234567
list.parallelStream().forEachOrdered(System.out::print);
}

浙公网安备 33010602011771号