JAVA8lambda表达式和stream流

真的是好久没有来写了,今天来总结一下传说中的lambda和stream

JAVA8 特性分析

  1. lambda表达式和函数式接口

  2. stream

lambda表达式和函数式接口

允许把函数当做参数传递给某个方法,或者把代码本身当作数据处理;(行为参数化)

语法格式:

(parameters)->expression 或者 (parameters)->{statements;}

简单的例子:

  • 不需要参数,返回值为5
() -> 5;
  • 接收两个指定参数类型为int型整数,返回他们的和
(int x,int y) -> x-y;
  • 接收一个string对象,打印输出,没有返回值
(String s) -> System.out.println(s);
  • [ ]

lambda表达式的重要特征:

  • 可选类型声明:不需要声明参数类型,编译器能够推断出参数的类型。

  • 可选的参数圆括号:只有一个参数的时候可以不用圆括号,但是多个参数的时候需要定义。

  • 可选的大括号:如果主体只包含一个语句,就不需要使用大括号。

  • 可选的返回关键字

lambda表达式的变量作用域:

  • 在lambda表达式中,只能引用标记为final的外层局部变量,也就是说在lambda表达式内部不可以修改外层局部变量,在外部也不可以改变,否则会编译错误。

    int portNumber = 1122;
    Runnable r = () -> System.out.println(portNumber);
    portNumber = 3222;
    
  • lambda表达式的体和嵌套块有相同的作用域,所以不允许声明一个与局部变量同名的参数或者局部变量。

 String first = "";
Comparator<String > comparator = (first,second) -> Integer.compare(first.length(), second.length());

函数式接口

只有一个抽象方法的接口,可以有默认方法(default关键字)和静态方法,这样的接口可以隐式转换为lambda表达式。函数式接口一般用注解@Functionallnterface标注。默认方法和抽象方法之间的区别在于抽象方法需要实现,而默认方法不需要,默认方法使得开发者可以在不破坏二进制兼容性的前提下,往现存接口中添加新的方法。

四个重要的函数式接口:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6fl0Zkeb-1569503124364)(D:\workfile\学习资料\Java学习\markdown\图片\函数式接口.png)]

lambda表达式的应用场景:

  1. 使用()-> {} 替换匿名内部类

    使用匿名内部类的方式实现:
    
    List words = Arrays.asList("apple", "banana", "pear");
    words.sort(new Comparator() {
     
        @Override
        public int compare(String w1, String w2) {
            return Integer.compare(w1.length(), w2.length());
        }
     
    });
    
    使用lambda表达式:
    
    words.sort(( w1, w2) -> Integer.compare(w1.length(), w2.length()));
    
  2. 以流水线的方式处理数据

    List integers = Arrays.asList(4, 5, 6,1, 2, 3,7, 8,8,9,10);
     
    List evens = integers.stream().filter(i -> i % 2 == 0)
            .collect(Collectors.toList()); //过滤出偶数列表 [4,6,8,8,10]
    List sortIntegers = integers.stream().sorted() .limit(5).collect(Collectors.toList());//排序并且提取出前5个元素 [1,2,3,4,5]
  3. 用内部迭代取代外部迭代

    内部迭代时,项目可以透明地并行处理,或者用更优化的顺序进行处理,外部迭代的方法很难做到。

    List features = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API");
    for (String feature : features) {
        System.out.println(feature); //外部迭代
    }
     
    List features = Arrays.asList("Lambdas", "Default Method", "Stream API",
     "Date and Time API");
    features.stream.forEach(n -> System.out.println(n)); //内部迭代
    
  4. 重构现有臃肿代码


实例分析:

排序

  • 原始的方法:
public class AppleCpmparator implements Comparatot<Apple>
{
    public int compare(Apple a1,Apple a2)
    {
        return a1.getWeight().compareTo(a2.getWeight());
    }
 }
 inventory.sort(new AppleComparator());

  • 使用匿名类:
inventory.sort(new AppleComparator<Apple>()
    {
        public int compare(Apple a1,Apple a2)
        {
            return a1.getWeight().compareTo(a2.getWeight());
         }
        
    });
  • 使用lambda表达式:
inventory.sort((a1,a2)>a1.getWeight().compaerTo(a2.getWeight()));

Comparator 具有一个叫做comparing的静态辅助方法,它可以接受一个Function来提取Comparable键值,并生成一个Comparator对象。所以可以把代码修改的更加紧凑一些:

inventory.sort(comparing((a) -> a.getWeight()));
  • 使用方法引用:
inventory.sort(comparing(Apple::getWeight));

stream

能够对集合对象进行各种串行或并行聚合的操作,比如排序、过滤、重组和数据统计等操作。

集合和流的差异在于什么时候进行计算,集合是一个内存中的数据结构,它包含数据结构中目前所有的值(集合中的每个元素都得先计算出来才能添加到集合中)。流则是在概念上固定的数据结构,不能添加或删除元素,其元素是按需计算的。流只能遍历一次,遍历完成后,就说这个流已经被消费掉了。

每一个stream都有两种模式:顺序执行和并行执行

顺序:

List list = Arrays.asList("a", "b", "c", "d");
Stream listStream = list.stream();        

并行:

Stream parallelListStream = list.parallelStream();

使用顺序方式去遍历时,每个item读完后再去读下一个item

使用并行方式去遍历时,数组会被分成多个段,其中每一个都在不同的线程中处理,然后将结果一起输出。

stream的创建方式:
  • 通过collection接口和数组的默认方法

    
    ArrayList users = new ArrayList<>();
    users.add(new User("2017001", false, 0));
    users.add(new User("2017002", true, 36));
    users.add(new User("2017003", false, 98));
    Stream stream = users.stream()
    
  • 通过stream接口的静态工厂方法(of、empty)

    Stream stream = Stream.of(1, 2, 3, 4);
    
stream的操作分为中间操作(Intermediate)和结束操作(Terminal):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7nWDlY92-1569503124366)(D:\workfile\学习资料\Java学习\markdown\图片\stream操作.png)]

  • 中间操作:一个流可以采用链式调用的方式进行数个中间操作,主要目的就是对这个流进行各种过滤、映射、聚集、统计操作等,每个操作结束后都会返回一个新的流,一次遍历就计算出所有结果。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nCIBB6Fu-1569503124366)(D:\workfile\学习资料\Java学习\markdown\图片\stream中间操作.png)]

  • 结束操作:一个流只能执行一个结束操作,当执行了结束操作以后这个流就不能再被执行。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Xv5uhONV-1569503124367)(D:\workfile\学习资料\Java学习\markdown\图片\stream结束操作.png)]

常见方法举例:

  1. filter

    filter 函数需要传入一个实现predicate函数式接口的对象,该接口的抽象方法test接收一个参数并返回一个booleann值,为true则保留,false则删除。

    List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
            numbers.stream()
                   .filter(i -> i % 2 == 0)
                   .distinct()
                   .forEach(System.out::println);
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YI7ELdiS-1569503124367)(D:\workfile\学习资料\Java学习\markdown\图片\数据流.png)]

  2. map

    一对一的映射,每输入一个数据也只会输出一个值。

    给定一个单词表,想要返回另一个列表,显示每个单词中有几个字母

    List<String> words = Arrays.asList("Hello", "World");
            List<Integer> wordLengths = words.stream()
                                             .map(String::length)
                                             .collect(toList());
    
  3. flatmap

    一对多的映射,对每个元素映射出来的依旧是一个stream

    给定一张单词表,如何返回一张列表,列出里面各不相同的字符,例如:给定单词表[‘Hello’,“World”],返回[“H”,“e”,“l”,“o”,“W”,“r”,“d”]

    先看看map的解决方法:

    List<String> words = Arrays.asList("Hello", "World");
    words.stream()
                .map(word->word.split(""))
                .distinct()
                .collect(Collectors.toList());
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dOT9Pvys-1569503124367)(D:\workfile\学习资料\Java学习\markdown\图片\map.png)]

    flatmap的解决方法:

    flatmap把一个流中的每个值都换成另一个流,然后把所有的流连接起来成为一个流。

     words.stream().map(w->w.split(""))
                .flatMap(Arrays::stream)
                .distinct()
                .collect(Collectors.toList());
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YiL8ujGG-1569503124368)(D:\workfile\学习资料\Java学习\markdown\图片\flatmap.png)]

你可能感兴趣的:(JAVA)