美文网首页
JAVA 8 IN ACTION

JAVA 8 IN ACTION

作者: LaMole | 来源:发表于2019-01-10 08:43 被阅读0次

    明天开始看,哈哈哈哈哈

    第一章

    为什么关注java 8
    1. 简化代码
      lambda替换匿名内

    2. 更简单的并行思路
      Stream API

    3. Stream可以看作是升级Java 8的主要原因,lambda和默认方法是对Stream的支持。(默认方法是由于JDK为了支持Stream,接口加入新方法,但是以前的代码没有实现了该接口,但是没有实现新增方法,如果没有默认方法,以前的代码将不可用)

    4. 函数式编程是指将代码传递给方法的功能


    1.1.2 流处理

    特性:流处理是并行的
    基石(限制):没有共享的可变数据(不支持可变的数据,虽然可以通过synchronized关键字使用可变数据,但是无法使用java 8带来的一系列关于流的优化),以及将代码传递给其他方法的能力
    ps. synchronized慢是因为触发了内核的,多处理器缓存一致性协议

    1.1.3 用行为参数化把代码传递给方法

    以排序为例,介绍了,在排序时,可以把不同的排序方法代码传递给方法

    1.2 JAVA 中的函数

    JAVA 8 新增了值的新形式--函数,可以传递给方法。

    File[] hiddenFiles = new File(".").listFiles(File::isHidden)
    

    由于为了只使用一两次的小方法,创建方法代码块会增加代码的混乱程度,所以出现了Lambda(匿名方法)
    例子

    # Before JAVA 8 
    ## compare with color
    public static List<Apple> filterGreenApples(List<Apple> inventory) {
        List<Apple> result = Lists.newArrayList();
        for(Apple apple : inventory) {
            if ("green".equals(apple.getColor())) {
                result.add(apple);
            }
        }
    }
    ## compare with weight
    public static List<Apple> filterHeavyApples(List<Apple> inventory) {
        List<Apple> result = Lists.newArrayList();
        for(Apple apple : inventory) {
            if (apple.getWeight() > 150) {
                result.add(apple);
            }
        }
    }
    # After JAVA 8
    ## 定义
    public static boolean isGreenApple(Apple apple) {
        return "green".equals(apple.getColor());
    }
    public static boolean isHeavyApple(Apple apple) {
        return apple.getWeight() > 150;
    }
    
    public static List<Apple> filterApples(List<Apple> inventory, Predicate<Apple> p) {
        List<Apple> result = Lists.newArrayList();
        for (Apple apple : inventory) {
            if (p.test(apple)) {
                result.add(apple);
            }
        }
        return result;
    }
    ## 调用
    filterApples(inventory, Apple::isGreenApple);
    filterApples(inventory, Apple::isHeavyApple);
    
    # Lambda
    ## 定义
    public static List<Apple> filterApples(List<Apple> inventory, Predicate<Apple> p) {
        List<Apple> result = Lists.newArrayList();
        for (Apple apple : inventory) {
            if (p.test(apple)) {
                result.add(apple);
            }
        }
        return result;
    }
    ## 调用
    filterApples(inventory, (Apple a) -> "green".equals(a.getColor()) );
    filterApples(inventory, (Apple a) -> a.getWeight() > 150 );
    

    ps.使用Lambda还是函数,要以代码的整洁易懂为准绳。

    1.3 流

    Stream API对于多核CPU有并发优化,并且在满足基石的条件下,基本有免费的并发效果.
    函数式编程的思想:

    1. 函数作为值传递
    2. 执行时元素之间无互动
    # 顺序处理
    List<Apple> heavyApples =
          inventory.stream().filter((Apple a) -> a.getWeight() > 150)
                                       .collect(toList());
    # 并行处理
    List<Apple> heavyApples =
          inventory.parallelStream().filter((Apple a) -> a.getWeight() > 150)
                                       .collect(toList());
    
    1.4 默认方法

    多个接口有相同方法的默认实现时,通过子类必须自己实现解决菱形继承问题。

    1.5 其他好思想

    模式匹配,还没看懂,后面补充

    // TODO
    

    第二章

    通过七种方法,展示了代码如何应对需求变化

    # 绿苹果
    # 1
    public static List<Apple> filterGreenApples(List<Apple> inventory) {
        List<Apple> result = Lists.newArrayList();
        for(Apple apple : inventory) {
            if ("green".equals(apple.getColor())) {
                result.add(apple);
            }
        }
    }
    # 各种颜色苹果,颜色单一维度
    # 2
    public static List<Apple> filterColorApples(List<Apple> inventory, String color) {
        List<Apple> result = Lists.newArrayList();
        for(Apple apple : inventory) {
            if (color.equals(apple.getColor())) {
                result.add(apple);
            }
        }
    }
    # 颜色,重量两个维度,再增加维度,还要修改代码
    # 3
    public static List<Apple> filterApples(List<Apple> inventory, String color, int weight, boolean flag) {
        List<Apple> result = Lists.newArrayList();
        for (Apple apple : inventory) {
            if ( (flag && color.equals(apple.getColor()))
                  || (!flag && apple.getWeight() > weight) ) {
                result.add(apple);
            }
        }
    }
    # 根据抽象条件筛选,每种不同的筛选行为,实现ApplePredicate接口
    # 4
    public static List<Apple> filterApples(List<Apple> inventory, ApplePredicate p) {
        List<Apple> result = Lists.newArrayList();
        for (Apple apple : inventory) {
            if (p.test(apple)) {
                result.add(apple);
            }
        }
    }
    
    public class AppGreenColorPredicate implements ApplePredicate {
        public boolean test(Apple apple) {
            return "green".equals(apple.getColor());
        }
    }
    
    filterApples(inventory, new AppGreenColorPredicate())
    # 使用匿名类调用,避免声明过多类
    # 5 与 4 的区别主要是在调用上
    public static List<Apple> filterApples(List<Apple> inventory, ApplePredicate p) {
        List<Apple> result = Lists.newArrayList();
        for (Apple apple : inventory) {
            if (p.test(apple)) {
                result.add(apple);
            }
        }
    }
    
    filterApples(inventory, new ApplePredicate() {
        @Override
        public boolean test(Apple apple) {
            return "green".equals(apple.getColor());
        }
    });
    # Lambda
    # 6 与 5 相比调用时候代码更加简单
    public static List<Apple> filterApples(List<Apple> inventory, ApplePredicate p) {
        List<Apple> result = Lists.newArrayList();
        for (Apple apple : inventory) {
            if (p.test(apple)) {
                result.add(apple);
            }
        }
    }
    
    filterApples(inventory, (Apple apple) -> "red".equals(apple.getColor()));
    # 范型方法, Predicate<T>范型接口为JAVA 8 内置接口
    # 7
    public static List<T> filterApples(List<T> inventory, Predicate<T> p) {
        List<T> result = Lists.newArrayList();
        for (T t : inventory) {
            if (p.test(t)) {
                result.add(t);
            }
        }
    }
    

    第三章

    基本语法:
    (parameters) -> expression //表达式lambda
    (parameters) -> { statements; }  //语句lambda
    

    Ps.
    (String s) -> { "IronMan"; } 不是Lambda表达式
    区别

    1. 表达式lambda可以转换为类型Expression<T>的表达式树,而语句lambda不可以
    Expression<Func<int, int, int>> expression = (a, b) => a + b;//正确
    Expression<Func<int, int, int>> expression1 = (a, b) => { return a + b; };//错误,无法将具有语句体的 lambda 表达式转换为表达式树
    
    1. 表达式lambda 可以兼容void类型
    #以下两个表达式都对
    Predicate<String> p = s -> list.add(s);
    Consumer<String> c = s -> list.add(s);
    
    哪里可以使用Lambda:

    函数式接口可以使用。函数式接口指的是,只有一个抽象方法的接口。可以用@FunctionalInterface标注,此注解主要用于编译期检查。

    函数描述符

    函数式接口的抽象方法的签名基本上就是Lambda表达式的签名
    Runnable 接口可以表示为() -> void,表示列表为空,返回为void的函数
    要注意Lambda的函数描述符和接口一致

    Lambda实践,环绕执行模式

    背景
    不同任务周围都围绕着相同的代码。比如处理文件的时候,先要初始化,然后再处理任务,最后要清理。
    实践

    public static String processFile() throws IOException {
        try (BufferedReader br = new BufferedReader(new FileReader("data.txt"))) {
            return br.readLine();
        }
    }
    
    String result = processFile((BufferedReader br) -> br.readLine() + br.readLine());
    
    public interface BufferedReaderProcessor {
        String process(BufferedReader b) throws IOException
    }
    
    public static String processFile(BufferedReaderProcessor p) throws IOException {
        try (BufferedReader br = new BufferedReader(new FileReader("data.txt"))) {
            return p.process(br);
        }
    }
    
    函数式接口的使用

    常用接口
    Predicate: T -> boolean
    Function: T-R
    Consumer: T->void
    原始类型参数化
    主要用于避免拆箱,适当提高性能
    装箱后,需要额外的内存,且需要内存搜索来获取原始值
    JDK提供了一些接口
    接口名的格式为:
    原始类型To原始类型Function,IntPredicate int -> boolean,等,可以搜索下
    异常处理
    Lambda不允许抛出任何受检查的异常,可以通过try...cache重新抛出运行时异常

    类型检查,类型推断以及限制
    1. 找到调用的函数的方法签名
    2. 找到传递Lamdba对应的参数类型
    3. 找到其抽象方法
    4. 匹配Lambda的函数描述符和抽象方法的参数列表
    相同的Lambda,不同的函数式接口

    Lambda表达式和函数式接口的匹配方式为参数列表匹配,只要参数列表相同,同一个Lambda表达式可以匹配多个函数式接口。
    void类型兼容
    表达式lambda 可以兼容void类型

    #以下两个表达式都对
    Predicate<String> p = s -> list.add(s);
    Consumer<String> c = s -> list.add(s);
    
    使用局部变量

    lambda可以使用局部变量,但是局部变量必须显示或者事实上final的,即声明为final或者只赋值过一次
    局部变量限制的原因
    实例变量在堆中,局部变量在栈中。如果Lambda可以直接访问局部变量,而且Lambda是在一个线程中使用的,则使用Lambda的线程,可能会在分配该变量的线程将变量收回之后,去访问变量。所以实际上访问的都是局部变量的副本。(本质上是为了解决内存问题)

    方法引用

    方法引用有三类

    1. 静态方法引用Integer::parseInt
    2. 指向任何类型方法的方法引用String::length
    3. 指向现有对象的实例方法的方法引用expensiveTransaction::getValue

    2 & 3的区别是

    # 2
    # 需要的Lambda
    (String s) -> s.toUppeCase()
    # 引用方法为
    String::toUppeCase()
    # 3
    # 需要的Lambda
    () -> expensiveTransaction.getValue()
    # 引用方法为
    expensiveTransaction::getValue
    

    区别是实例本身是不是Lambda的一个参数,不是就用实例::方法,否则用类::方法


    三种方法
    构造函数的引用

    Class::new根据参数个数赋值给函数式接口,最好调用函数式接口的方法进行实例化


    第四章

    中间操作与终端操作
    中间操作

    Operation Type Return type Argument operation Function descriptor
    filter Intermediate Stream<T> Predicate<T> T -> boolean
    sort Intermediate Stream<R> Function<T, R> T -> R
    limit Intermediate Stream<T>
    sorted Intermediate Stream<T> Comparator<T> (T, T) -> int
    distinct Intermediate Stream<T>

    终端操作

    Operation Type Purpose
    forEach Terminal Consumes each element from a stream and applies a lambda to each of them. The operation returns void.
    count Terminal Returns the number of elements in a stream. The operation returns a long.
    collect Terminal Reduces the stream to create a collection such as a List, a Map, or even an Integer. See chapter 6 for more detail.

    第五章

    谓词
    一个返回boolean的函数

    筛选 & 切片
    1. filter(Predicate<T>),接受一个谓词,根据谓词返回结果筛选
    2. distinct(),返回无重复元素
    3. limit(n),截断流至前n个
    4. skip(n),跳过前n个元素
    映射
    1. map(Function<T, R>),对流中的每一个元素做Function操作,得到一个新的范型类型的流Stream<T> -> Stream<R>
    2. 流的扁平化
      背景
      将字符串列表拆分为字符流
      背景
      方案1
      该方法将会得到一个Stream<String[]>因为word -> word.split("") 对每个单词返回一个String[]
      Ps.word -> word.split("") 不可写为String::split("")。
      String::split("")的函数描述符是(String a, String b) -> a.split("b")不符合Function<T, R> 的 T -> R
      所以,后续的distinct和collect都是对Stream<String[]>的操作。
      方案
      Stream<String> flatMap(Stream<String>)
      flatMap将每个输入的Stream<String> 合并成一个Stream<String>

    anyMatch(Predicate<T>), 查找任意一个
    allMatch((Predicate<T>)), 全部匹配
    noneMatch(Predicate<T>),没有任何匹配
    findAny(),返回流中任意元素
    findFirst(),返回流中第一个元素
    findAny VS findFirst
    并行计算时表现不一样,如果不关心顺序尽量用findAny
    Optional<T>,主要用于解决null问题。
    isPresent(),包含值返回true,否则返回false
    ifPresent(Consumer<T> block),值存在则执行代码。
    get(),存在则返回值,否则抛NoSuchElement异常
    T orElse(T other),值存在则返回值,否则返回默认值
    reduce,第一个参数为初始值,第二个参数为BinaryOperator<T>.

    数值流

    mapToInt() 映射到int省去拆/装箱成本

    构建流

    Stream.of 由值创建流
    Stream.empty() 创建一个空流
    Arrays.stream() 由数组创建流
    Files.lines(Paths.get(path), Charset.defaultCharset()) 由文件创建流
    Stream.iterate() 由函数生成无界流, 接收迭代器
    Stream.generate() 由函数生成无界流, 接收函数

    相关文章

      网友评论

          本文标题:JAVA 8 IN ACTION

          本文链接:https://www.haomeiwen.com/subject/gkkirqtx.html