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);
    }

posted @ 2021-02-23 10:24  荞麦假面  阅读(35)  评论(0)    收藏  举报