java8新特性之lambda表达式和stream流常用方法探索

lambda表达式探索

简介

学习lambda表达式就要先知道函数式接口是什么

函数式接口(Functional Interfaces):如果一个接口定义个唯一一个抽象方法,那么这个接口就成为函数式接口。同时,引入了一个新的注解:@FunctionalInterface。可以把他它放在一个接口前,表示这个接口是一个函数式接口。这个注解是非必须的,只要接口只包含一个方法的接口,虚拟机会自动判断,不过最好在接口上使用注解 @FunctionalInterface 进行声明。在接口中添加了 @FunctionalInterface 的接口,只允许有一个抽象方法,否则编译器也会报错。

示例:

     /**
      * 函数式接口
      */
        @FunctionalInterface
        interface Sum{
            int add(int value);
        }

Lambda表达式:可以让你的代码更加的简洁。ambda无法单独出现,需要一个函数式接口来盛放,可以说lambda表达式方法体是函数式接口的实现,lambda实例化函数式接口,可以将函数作为方法参数,或者将代码作为数据对待。

主要优点:

1.代码变得更加简洁紧凑
2.可读性强,
3.并行操作大集合变得很方便,可以充分发挥多核cpu的优势,更加便于多核处理器编写代码等,语法

Lambda语法

(parameters)->expression 或者 (parameters)->{statements;}
Lambda表达式由三部分组成:
1.parameters:类似方法中的形参列表,这里的参数是函数式接口里的参数。这里的参数类型可以明确的声明也可不声明而由JVM隐含的推断,当只有一个推断类型时可以省略掉圆括号。
2.-> :可以理解为“被用于”的意思
3.方法体:可以是表达式也可以是代码块,实现函数式接口中的方法。这个方法体可以有返回值也可以没有返回值

示例:

1.不接受参数,直接返回1
    ()->1
2.接受两个int类型的参数,返回这两个参数的和
    (int x,int y )-> x+y
3.接受x,y两个参数,JVM根据上下文推断参数的类型,返回两个参数的和
    (x,y)->x+y
4.接受一个字符串,打印该字符串,没有返回值
    (String name)->System.out.println(name)
5.接受一个参数,JVM根据上下文推断参数的类型,打印该参数,没有返回值,只有一个参数可以省略圆括号
   name->System.out.prinln(name)
6.接受两个String类型参数,分别输出,没有返回值
    (String name,String sex)->{System.out.println(name);System.out.println(sex)}
7.接受呀一个参数,返回它本身的2倍
    x->2*x

访问权限

在Lambda表达式使用中,Lambda表达式外面的局部变量会被JVM隐式的编译成final类型,Lambda表达式内部只能访问,不能修改
Lambda表达式内部对静态变量和成员变量是可读可写的
Lambda不能访问函数接口的默认方法,在函数接口中可以添加default关键字定义默认的方法

局部变量示例:

 public static void main(String[] args) {
        int num = 6;//局部变量
        Sum sum = value -> {
//            num = 8; 这里会编译出错
            return num + value;
        };
        sum.add(8);
    }
 
    /**
     * 函数式接口
     */
    @FunctionalInterface
    interface Sum{
        int add(int value);
    }

静态变量和成员变量示例:

 public int num1 = 6;
    public static int num2 = 8;
    private int getSum(){
        Sum sum = value -> {
            num1 = 10;
            num2 = 10;
            return  num1 + num2;
        };
        return sum.add(1);
    }
 
    /**
     * 函数式接口
     */
    @FunctionalInterface
    interface Sum{
        int add(int value);
    }

方法引用

在lambda表达式中,方法引用是一种简化写法,引用的方法就是Lambda表达式的方法体的实现
语法结构:ObjectRef:: methodName
左边是类名或者实例名,中间的“::”是方法引用符号,右边是相应的方法名
方法引用一般分为三类:
静态方法引用,实例方法引用,构造方法引用

静态方法引用示例:

public static void main(String[] args){
        //传统方式
        Transform<String ,Integer> transform1 = new Transform<String, Integer>() {
            @Override
            public Integer transform(String s) {
                //方法引用之静态方法引用
                return A.strToInt(s);
            }
        };
        int result1 = transform1.transform("100");
 
        //Lambda方式:方法引用之静态方法引用
        Transform<String,Integer> transform2 = A ::strToInt;
        int result2 = transform2.transform("200");
    }
 
    static int strToInt(String str){
        return Integer.valueOf(str);
    }
 
    /**
     * 函数式接口
     * @param <A>
     * @param <B>
     */
    @FunctionalInterface
    interface Transform<A,B>{
        B transform(A a);
    }

实例方法引用示例:

public static void main(String[] args){
        //传统方式
        Transform<String ,Integer> transform1 = new Transform<String, Integer>() {
            @Override
            public Integer transform(String s) {
                return new Obj().strToInt(s);
            }
        };
        int result1 = transform1.transform("100");
        //Lambda方式
        Obj obj = new Obj();
        Transform<String,Integer> transform2 = obj::strToInt;
        int result2 = transform2.transform("200");
    }
    /**
     * 函数式接口
     * @param <A>
     * @param <B>
     */
    interface Transform<A,B>{
        B transform(A a);
    }
    /**
     * 实例对象类
     */
    static class Obj{
        public int strToInt(String str){
            return Integer.valueOf(str);
        }
    }

构造方法引用示例:

 //传统方式
        Factory factory1 = new Factory() {
            @Override
            public Parent create(String name, int age) {
                return new Boy(name,age);
            }
        };
        Boy boy = (Boy) factory1.create("aa",18);
        factory1 = new Factory() {
            @Override
            public Parent create(String name, int age) {
                return new Girl(name,age);
            }
        };
        Girl girl = (Girl) factory1.create("bb",18);
        //Lambda方式
        Factory<Boy> boyFactory = Boy::new;
        Boy boy1 = boyFactory.create("cc",18);
        Factory<Girl> girlFactory = Girl::new;
        Girl girl1 = girlFactory.create("dd",18);

四个常用的接口

Predicate接口

/**
 * Predicate接口:输入一个参数,返回一个boolean值,内置了许多用于逻辑判断的默认方法
 */
public class F_实践之Predicate {
    public void predicateTest(){
        Predicate<String> predicateStr = s -> s.length()>8;
        boolean testResult = predicateStr.test("test");//需要api 24
        testResult = predicateStr.negate().test("test");//取反,也就是s.length<=8
 
        Predicate<Object> predicateObj = Objects::nonNull;
        Object obj = null;
        testResult = predicateObj.test(obj);//判断是否为空
    }
}

Consumer接口

/**
 * consumer接口:对输入的参数进行操作。有输入没输出
 */
  private static void consumerTest(){
        Consumer<Integer> add5 = (p) -> {
            System.out.println("old value:" + p);
            p = p + 5;
            System.out.println("new value:" + p);
        };
        add5.accept(10);
    }

Function接口

/**
 * Function接口:接受一个参数,返回单一的结果。默认的方法(andThen)可将多个函数串在一起,形成复合Funtion(有输入,有输出)结果
 */
  public static void functionTest(){
        Function<String, Integer> toInteger = Integer::valueOf;
        //toInteger的执行结果作为第二个backToString的输入
        Function<String, String> backToString = toInteger.andThen(String::valueOf);
        String result = backToString.apply("1234");
        System.out.println(result);
 
        Function<Integer, Integer> add = (i) -> {
            System.out.println("frist input:" + i);
            return i * 2;
        };
        Function<Integer, Integer> zero = add.andThen((i) -> {
            System.out.println("second input:" + i);
            return i * 0;
        });
 
        Integer res = zero.apply(8);
        System.out.println(res);
    }

Supplier接口

/**
 * Supplier接口:返回一个给定类型的结果。不需要输入参数,无输入有输出
 */
   private static void supplierTest(){
        Supplier<String> supplier = () -> "输出";
        String s = supplier.get();
        System.out.println(s);
    }

stream探索

1.串行stream操作

Lambda为java8带来了闭包,支持对集合对象的stream进行函数式操作, stream api被集成进了collection api ,允许对集合对象进行批量操作。
Stream表示数据流,它没有数据结构,本身也不存储元素,其操作也不会改变源Stream,而是生成新Stream.作为一种操作数据的接口,它提供了过滤、排序、映射、规约等多种操作方法,
这些方法按照返回类型被分为两类:凡是返回Stream类型的方法,称之为中间方法(中间操作),其余的都是完结方法(完结操作)。完结方法返回一个某种类型的值,而中间方法则返回新的Stream。
中间方法的调用通常是链式的,该过程会形成一个管道,当完结方法被调用时会导致立即从管道中消费值,这里我们要记住:Stream的操作尽可能以“延迟”的方式运行,也就是我们常说的“懒操作”,
这样有助于减少资源占用,提高性能。对于所有的中间操作(除sorted外)都是运行在延迟模式下。

Stream不但提供了强大的数据操作能力,更重要的是Stream既支持串行也支持并行,并行使得Stream在多核处理器上有着更好的性能。

Stream的使用过程有着固定的模式:

1.创建Stream
2.通过中间操作,对原始Stream进行“变化”并生成新的Stream
3.使用完结操作,生成最终结果

 //创建一个集合
 List<String> list = new ArrayList<>();
      list.add("a1");list.add("a2");list.add("a3");list.add("b1");list.add("b2");list.add("b3");

中间操作方法

过滤(filter)

结合Predicate接口,Filter对流对象中的所有元素进行过滤,该操作是一个中间操作,这意味着你可以在操作返回结果的基础上进行其他操作

 public static void sreamFilterTest(List<String> lists){ //要明确这list的泛型类型,否则jvm不能根据上下文确定参数类型
        lists.stream().filter((s -> s.startsWith("a"))).forEach(System.out::println);//将开头是a的过滤出来
 
        //等价于以上操作
        Predicate<String> predicate = (s) -> s.startsWith("a");//将开头是a的过滤出来
        lists.stream().filter(predicate).forEach(System.out::println);
 
        //连续过滤
        Predicate<String> predicate1 = (s -> s.endsWith("1"));//将开头是a,并且结尾是1的过滤出来
        lists.stream().filter(predicate).filter(predicate1).forEach(System.out::println);
    }
排序(sorted)

结合Comparator,该操作返回一个排序过后的流的视图,原始流的顺序不会改变。通过Comparator来指定排序规则,默认是自然排序

sorted有两种方法,一种是不传任何参数,叫自然排序,还有一种需要传Comparator 接口参数,叫做定制排序。

 //自然排序
        List<String> collect = list.stream().sorted().collect(Collectors.toList());
 //定制排序
        List<String> collect2 = list.stream().sorted((o1, o2) -> {
            if(o1.length()>o2.length()){
                return 1;
            }else 
            if(o1.length()<o2.length()){
                return -1;
            }else {
                return 0
            }
        }).collect(Collectors.toList());
映射(map)

结合Function接口,该操作能将流对象中的每一个元素映射为另一个元素,实现元素类型的转换。

 private static void streamMapTest(List<String> list){
        list.stream().map(String::toUpperCase).sorted((s, t1) -> 	t1.compareTo(s)).forEach(System.out::println);
        System.out.println("- - - - - - ");
        //自定义映射规则
        Function<String,String> function = s -> {return  s + ".map3";};
        list.stream().map(function).forEach(System.out::println);
    }
匹配(match)

用来判断某个predicate是否和流对象相匹配,最终返回boolean类型的结果

private static void streamMatchTest(List<String> list){
        //流对象中只要有一个元素匹配就返回true
        boolean anyStartWithA = list.stream().anyMatch(s -> s.startsWith("a"));
        System.out.println("集合中是否有以'a'来头:"+ anyStartWithA);
        //流对象中每一个元素都匹配才返回true
        boolean allStartWithA = list.stream().allMatch(s -> s.startsWith("a"));
        System.out.println("集合中每一个都是以'a'开头:"+ allStartWithA);
        //流对象中没有匹配时返回true
        boolean noneStartWithA = list.stream().noneMatch(s -> s.startsWith("c"));
        System.out.println("集合中没有以'c'开头:"+ noneStartWithA);
    }
收集(collect)

在对经过变换后,将变换的stream元素收集,比如将这些元素存在集合中,可以使用stream提供的collect方法

 private static void streamCollectTest(List<String> list){
        List<String> listNew = list.stream().filter(s -> s.startsWith("b")).sorted().collect(Collectors.toList());
        System.out.println(listNew );
    }
计数(count)

用来统计流中元素的总数

private static void streamCountTest(List<String> list){
        long count = list.stream().filter(s -> s.startsWith("b")).count();
        System.out.println("以'b'开头的数量:"+ count);
    }
截断(limit)

可以配合其他得中间操作,并截断流,取到相应的元素个数,这不会往下执行,可以提高效率

List<String> list = Arrays.asList("1","2","3","4","0","5","6","7","8");
 Stream<String> stream = list.stream().filter((x) -> {
            System.out.println(" api  中建操作。");
            return x.equals("3");
        });
        //取两个 , 可以配合其他得中间操作,并截断流,取到相应的元素个数,这不会往下执行,可以提高效率
        stream.limit(2).forEach(System.out::println);
跳过元素(skip)

skip(n),返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空,与limit(n)互补。

List<String> list = Arrays.asList("1","2","3","4","0","5","6","7","8");
        Stream<String> stream = list.stream().filter((x) -> {
            System.out.println(" api  中建操作。");
            return x.equals("3");
        });
        //skip(n),返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空,与limit(n)互补。
        stream.skip(3).limit(1).forEach(System.out::println);

筛选(distinct )

distinct 通过流所生成元素的hashCode()和equals()去除重复元素

List<String> list = Arrays.asList("1","2","3","4","0","5","6","7","8");
 Stream<String> stream = list.stream();
        stream.distinct().forEach(System.out::println);

2.stream 终止操作

示例实体类:

public class Person {
    String name ;
    String sex ;
    int age;
    Status statusEnum;

    public Person(String name, String sex, int age, Status statusEnum) {
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.statusEnum = statusEnum;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Status getStatusEnum() {
        return statusEnum;
    }

    public void setStatusEnum(Status statusEnum) {
        this.statusEnum = statusEnum;

}

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ", age=" + age +
                ", statusEnum=" + statusEnum +
                '}';
    }
}

示例操作:

 List<Person> persons = Arrays.asList(
                          new Person("小红", "男", 76, Status.FREE),
              new Person("小明", "女", 12, Status.BUSY),
               new Person("小白", "男", 35, Status.BUSY),
               new Person("小李", "男", 3, Status.FREE),
               new Person("小花", "男", 56, Status.BUSY),
               new Person("如花", "女", 34, Status.VOCATION),
               new Person("似玉", "女", 34, Status.FREE),
               new Person("芙蓉", "女", 34, Status.VOCATION)
              );

1.查找与匹配

allMatch :检查是否匹配所有元素
    public void test1(){
        boolean allMatch = persons.stream().allMatch((x) -> {
            return x.getStatusEnum().equals(Status.FREE);
        });
        System.out.println(allMatch);
    }
anyMatch :检查是否至少匹配一个元素
    public void test2(){
        boolean allMatch = persons.stream().anyMatch((x) -> {
            return x.getStatusEnum().equals(Status.FREE);
        });
        System.out.println(allMatch);
    }
noneMatch :检查是否没有匹配所有元素
   // 检查 所有的是否都是 FREE  ----- 结果是false
   public void test3(){
        boolean allMatch = persons.stream().noneMatch((x) -> {
            return x.getStatusEnum().equals(Status.FREE);
        });
        System.out.println(allMatch);
    }
findFirst :返回第一个元素
 public void test4(){
      Optional<Person> first = persons.stream().findFirst();java

      System.out.println(first.get());
  }
findAny : 返回当前流中任意元素
public void test5(){
     Optional<Person> first = persons.stream().findAny();
     //first.orElse(new Person());  如果没空 可以创一个新的对象去代替它
     System.out.println(first.get());
 }
count : 返回流中元素总个数
  public void test6(){
      long first = persons.stream().count();
      System.out.println(first);
  }
max :返回流中最大值
public void test7(){
     Optional<Person> person = persons.stream().max((x,y) ->  Integer.compare(x.age, y.age));
     System.out.println(person.get());
 }
min : 返回流中最小值
public void test8(){
      Optional<Person> person = persons.stream().min((x,y) ->  Integer.compare(x.age, y.age));
      System.out.println(person.get());
  }

2.归约 (reduce)

可以将流中元素反复结合在一起,得到一个值

reduce(T identitty,BinaryOperator)首先,需要传一个起始值,然后,传入的是一个二元运算。

  public void test9(){
       List<Integer> list = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
               // identitty 起始值 0 ,然后与集合中的值进行 相应的运算,再次赋值给 identity 然后 在进行运算。
                Integer sum = list.stream().reduce(0, (x, y) -> x + y);
       System.out.println(sum);
   }

reduce(BinaryOperator)此方法相对于上面方法来说,没有起始值,则有可能结果为空,所以返回的值会被封装到Optional中。

//map和reduce的连接通常称为map-reduce模式,因Google用它来进行网络搜索而出名。
//用map 来提取 对象中某个属性,然后再用reduce 进行归约。
    public void test10() {
        Optional<Integer> reduce = persons.stream().map(Person::getAge).reduce(Integer::sum);
        System.out.println(reduce.get());
    }

3.收集(collect)

收集 : 收集collect(将流转换为其他形式。接收一个Collector接口得实现,用于给其他Stream中元素做汇总的方法)

Collector接口中方法得实现决定了如何对流执行收集操作(如收集到List,Set,Map)。
但是Collectors实用类提供了很多静态方法,可以方便地创建常见得收集器实例。

Collectors.toList() 将流转换成List
 public void test11() {
     List<Person> collect = persons.stream().collect(Collectors.toList());
     collect.forEach(System.out::println);
 }
将流转换成HashSet
  public void test12() {
      //hashset会去重复
          Set<String> collect = persons.stream().map(Person::getName).collect(Collectors.toSet());
          collect.forEach(System.out::println);
      }
将流转换成其他集合
public void test13() {
      Set<Integer> collect = persons.stream().map(Person::getAge).collect(Collectors.toCollection(LjavainkedHashSet::new));
      collect.forEach(System.out::println);
  }
Collectors.counting() 元素个数
 public void test14() {
      Long collect = persons.stream().map(Person::getAge).collect(Collectors.counting());
      System.out.println(collect);java
  }
将流转换为其他形式

将流转换为其他形式 , 接受一个conllectors接口的实现,用于给Stream中元素

public void test14s() {
        // 1 对元素进行汇总方法
        DoubleSummaryStatistics collect = persons.stream().collect(Collectors.summarizingDouble(Person::getAge));
        IntSummaryStatistics collect2 = persons.stream().collect(Collectors.summarizingInt(Person::getAge));
        System.out.println(collect.getMax());
        System.out.println(collect.getAverage());
        System.out.println(collect.getCount());
        System.out.println(collect.getMin());
        System.out.println(collect.getSum());
将元素转换为其他形式
       String collect1 = persons.stream().map(Person::getName).collect(Collectors.joining(",","头","尾"));
        String collect3 = persons.stream().map(Person::getName).collect(Collectors.joining());
        System.out.println(collect1); //头小红,小明,小白,小李,小花,如花,似玉,芙蓉尾
        System.out.println(collect3); // 小红小明小白小李小花如花似玉芙蓉
    }
平均数
    1. Collectors.averagingDouble()
    2  Collectors.averagingDouble()
    3  Collectors.averagingLong()
    平均数,这三个方法都可以求平均数,不同之处在于传入得参数类型不同,返回值都为Double
    
    public void test15() {
        Double s = persons.stream().collect(Collectors.averagingDouble(Person::getAge));
        System.out.println(s);
    }
Collectors.maxBy() 求最大值
      public void test16() {
          Optional<Person> collect = persons.stream().collect(Collectors.maxBy((o1, o2) -> Integer.compare(o1.age, o2.age)));
          System.out.println(collect.get().age);
      }
Collectors.minBy() 求最小值
 public void test17() {
     Optional<Person> collect = persons.stream().collect(Collectors.minBy((o1, o2) -> Integer.compare(o1.age, o2.age)));
     System.out.println(collect.get().age);
 }
Collectors.groupingBy()分组 ,返回一个map
  //按照 Status 分组
  public void test18() {
      Map<Status, List<Person>> collect = persons.stream().collect(Collectors.groupingBy(Person::getStatusEnum));
      collect.forEach((status, people) -> {
          System.out.println(" status === " + status);
          people.forEach(System.out::println);
      });
  }
多级分组

Collectors.groupingBy()还可以实现多级分组

 public void test19() {

     Map<Status, Map<String, List<Person>>> collect = persons.stream().collect(Collectors.groupingBy(Person::getStatusEnum,Collectors.groupingBy(Person::getSex)));
     Map<Status, Map<String, List<Person>>> collect2 = persons.stream().collect(Collectors.groupingBy(Person::getStatusEnum,Collectors.groupingBy(
             e->{
                 if (e.getAge()>10){
                     return "小孩";
                 }else {
                     return "大人";
                 }
             }
     )));
     System.out.println(collect);
     System.out.println(collect2);
 }
分区
//Collectors.partitioningBy() 分区,参数中传一个函数,返回true,和false 分成两个区
public void test20() {
//年龄大于39的分区  不满足再的分区
          Map<Boolean, List<Person>> collect = persons.stream().collect(Collectors.groupingBy(e -> e.getAge() > 30));
      System.out.println(collect);
  }

3.并行操作stream

并行Stream:基于Fork-join并行分解框架实现,将大数据集合切分为多个小数据结合交给不同的线程去处理,这样在多核处理情况下,性能会得到很大的提高。
这和MapReduce的设计理念一致:大任务化小,小任务再分配到不同的机器执行。只不过这里的小任务是交给不同的处理器。
结果是性能提高50%,单核下还是串行流性能比较好,并行流的使用场景是多核+大数据

//创建一个大集合
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 10000000; i++) {
            UUID uuid = UUID.randomUUID();
            list.add(uuid.toString());
        }
//并行stream
    private static void parallelStreamSortedTest(List<String> list){
        long startTime = System.nanoTime();//返回最准确的可用系统计时器的当前值,以毫微秒为单位。
        long count = list.parallelStream().sorted().count();
        long endTime = System.nanoTime();
        long millis = TimeUnit.NANOSECONDS.toMillis(endTime - startTime);
        System.out.printf("并行排序花费时间:%d ms",millis);
    }
//串行stream
    private static void streamSortedTest(List<String> list){
        long startTime = System.nanoTime();//返回最准确的可用系统计时器的当前值,以毫微秒为单位。
        long count = list.stream().sorted().count();
        long endTime = System.nanoTime();
        long millis = TimeUnit.NANOSECONDS.toMillis(endTime - startTime);
        System.out.printf("串行排序花费时间:%d ms",millis);
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值