Java8实战笔记

只是笔记 笔记 不是教程

lambda表达式结构为
参数 箭头 代码主体

函数式接口就是只定义一个抽象方法的接口

函数式接口的抽象方法的签名叫做函数描述符

Java8中常用的函数式接口
函数式接口      函数式描述符     原始类型特化
Predicate          T -> boolean    IntPredicate等
Function           T -> R     IntToDoucleFunction等    
Consumer         T -> void            IntConsumer等
Suplier            () -> T               DoubleSupplier等
UnaryOperator  T->T                IntUnaryOpearator等
BinaryOperator  (T,T) -> T         IntBinaryOperator等
BiPredicate         (L,R)->boolean   
BiConsumer       (L,R)->void      ObjIntConsumer等
BiFunction        (T,U) -> R         ToDoubleBiFunction等 

闭包就是一个函数的实例,且它可以无限制地访问那个函数的非本地变量。
方法引用是如果一个Lambda代表的只是“直接调用这个方法”,那最好还是用名称来调用它,而不是去描述如何调用它。

流的定义是从支持数据处理操作的源生成的元素序列
 集合讲的是数据,流讲的是计算
 流会使用一个提供数据的源,如集合,数组或输入输出资源
特点
 流水线的操作可以看做对数据源进行数据库式查询
 内部迭代 - 流的迭代操作时在背后进行的

集合与流之间的差别在于什么时候进行计算.
 集合是一个内存中的数据结构, 它包含了数据结构中目前所有的值
 流是概念上固定的结构数据结构(不能添加与删除) 元素是按需计算的, 流就像一个延迟创建的集合, 只有在消费者要求的时候才会计算值, 与此相反, 集合则是急切创建的

流只能遍历一次, 遍历完后 这个流就已经被消费掉了, 可以从数据源那里再获取一个新的流重新遍历, 流只能被消费一次!

使用Collection接口需要用户去做迭代比如foreach,这称为外部迭代.相反,Streams库使用内部迭代 他帮你把迭代做了,还把得到的流值存在了某个地方,你只需要给出一个函数说要干什么就可以了


Java8需要一个类似于Collec却没有迭代器的接口(需要内部迭代自己做一些优化),于是就有了Stream

可以连起来的操作称为中间操作,关闭流的操作称为终端操作
中间操作会返回另一个流,这让多个操作可以连接起来形成一个查询, 除非流水线上触发一个终端操作,否则中间操作不会执行任何处理,这是因为中间操作一般可以合并起来,在终端操作时一次性全部处理

终端操作会从流的流水线生成结果
终端操作会返回非stream的值,终中间操作会返回stream

流的使用包括三件事
1.一个数据源来执行一个查询 2.一个中间操作链,形成一条流的流水线 3.一个终端操作,执行流水线,并生成结果

流的api
1. 筛选 filter 获取符合条件的元素
2. 去重 distinct (使用hashCode和equals判断重复)
3. 截断 limit(n) 返回一个不超过给定长度的流 是一个中间操作
4. 跳过元素 skip(n) 返回一个扔掉前n个元素的流,如果流中元素不足n个,则返回空流
5. 映射(转换) map 方法参数是function , 接收一个函数作为参数, 这个函数会被应用到每个元素上, 并将其映射成一个新的元素
6. 查找与匹配
 anyMatch 检查谓词是否至少匹配一个元素
    boolean exist = appleList.stream().anyMatch((apple) -> "color".equals(apple.getColor()));

 allMatch 检查谓词是否匹配所有元素
 noneMatch 确保流中的任何元素没有与给定的谓词匹配
短路求值: 有些操作不需要处理整个流就能得到结果 以上match都用到了短路 
 findAny 返回当前流中的任意元素

Optional 是一个容器类,代表一个值存在或不存在
 isPresent() 有值返回true 否则返回false
 isPersent(Consumer <T> block) 有值的时候执行Consumer
 T get() 会返回值, 值不存在抛出异常
 T orElse(T other) 存在返回值 不存在返回默认值

  findFirst 查找第一个元素

7. 规约 reduce(初始值,BinaryOperator<T>)

原始类型流特化
3个 IntStream DoubleStream LongStream 避免了装箱操作
映射到数值流 stream.mapToInt maoToDouble maoToLong
转换为对象流 intStream.boxed();

数值范围 
IntegerStream.rangeClosed(1,100) 返回一个1到100的流

构建流 
1. 由值创建流 Stream<String> stream = Stream.of("Java8","Java11")
2. 空流 Stream<String> stream = Stream.empty();
3. 由数组创建流 Arrays.stream(arr);
4. 文件流 Files.lines
5. 函数创建流
  无限流 Stream.iterate 和 Stream.generate
  需要生成一系列值的时候应该使用iterate 


函数式编程相对于指令式编程的主要优势 只需要指出希望的结果 做什么 而不需要操作执行的步骤 如何做

预定义收集器(Collectors提供的工厂方法创建收集器)主要提供了三个功能 1.将流元素归约和汇总为一个值 2.元素分组 3.元素分区
 - Collectors.counting() 返回流中的个数 可省略为stream.count()
 - Collectors.maxBy | minBy 查找流中的最大值与最小值 参数是比较器
 - Collectors.summingInt | Long | Double 汇总 求和
 - Collectors.averagingInt 求平均数
 - Collectors.joining() 把流中的每一个对象的toString连接字符串, 带参数的为链接的分隔符
 - Collectors.reducing 
 - Collectors.groupingBy() 分组
    条件分组 
   groupingBy(dish -> {
    if(dish.getWeight > 500){
       return xxx
     }
   })

  多级分组   第二个参数再加个Collectors.groupingBy()
  按子组收集数据 第二个参数 Collectors.counting()
 - Collectors.mapping 映射
 - Collectors.partitioningBy 分区 键为boolean


自定义收集器
接口泛型: T是流中要收集的项目的泛型, A是累加器的类型, R是收集操作得到的对象

public class SunToListCollector<T> implements Collector<T, List<T>, List<T>> {

    /**
     * 建立新的结果容器
     */
    @Override
    public Supplier<List<T>> supplier() {
        return ArrayList::new;
    }

    /**
     * 将结果添加至结果容易
     */
    @Override
    public BiConsumer<List<T>, T> accumulator() {
        return List::add;
    }

    /**
     * 合并两个结果容器
     */
    @Override
    public BinaryOperator<List<T>> combiner() {
        return (list1, list2) -> {
            list1.addAll(list2);
            return list1;
        };
    }

    /**
     * 对结果容器应用最终转换
     */
    @Override
    public Function<List<T>, List<T>> finisher() {
        return Function.identity();
    }

    @Override
    public Set<Characteristics> characteristics() {
        return Collections.unmodifiableSet(EnumSet.of(Characteristics.IDENTITY_FINISH, Characteristics.CONCURRENT));
    }
}

自定义收集器而不去实现
 appleList.stream().collect(ArrayList::new, List::add, List::addAll);

并行流 stream.parallel() 顺序流 stream.sequential() 一个流水线只能使用一个方式 因为后面的会覆盖掉前面的
配置并行流使用的线程池 (不建议修改)
内部使用的是ForkJoinPool 默认的线程数量就是处理器数量 值由Runtime.getRuntime().available-Processors()得到
可以使用System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism","12"); 配置


匿名类和lamdba表达式中的this和super的含义不同, 匿名类中this代表类自身,lamdba中this代表包含类
匿名类可以屏蔽包含类的变量, lambda不能
优先使用静态辅助方法, 这些方法设计之初就考虑了会结合方法引用一起使用
建议所有的外部迭代转换为stream api式 因为stream api更清晰的表达数据处理管道的意图
增加代码灵活性
 1. 采用函数式接口: 没有函数式接口就无法使用lambda表达式
 2. 环绕执行: 虽然你的业务代码千差万别: 但是他们拥有同样的准备和清理阶段, 这是可以将这部分代码用lamdba代码实现

将复杂的lambda转为方法引用

-------------------------------

设计模式

策略模式
    策略模式代表了解决一类算法的通用解决方案,你可以在运行时选择使用哪种方案
三部分内容
1.一个代表某种算法的接口
    2. 一个或多个该接口的算法实现
3. 一个或多个使用策略对象的客户

模板方法
    希望使用一个东西,但是只对其中的某些行改进,达到希望的效果
观察者模式
    某些事件发生时(或状态改变),一个对象需要自动的通知其他多个对象
责任链模式
    责任链模式是一种处理对象序列的通用方案,一个处理对象可能需要完成一些工作之后,将结果传递给另一个对象,这个对象接着做一些工作,再转交给下一个处理对象,以此类推


public abstract class ProcessingObject<T> {

    private ProcessingObject<T> processingObject;


    public void setProcessingObject(ProcessingObject<T> processingObject) {
        this.processingObject = processingObject;
    }

    public T handler(T input) {
        T t = handlerWork(input);
        if (processingObject != null) {
            return processingObject.handler(input);
        }
        return t;
    }

    protected abstract T handlerWork(T input);

}


Java8可对接口进行默认实现 需加关键字default, 接口默认方法就是为了兼容avaApi类库演进问题
Java是单继承多实现
可选方法: 只需要实现需要做的方法 不需要的不再理会 更不需要定义一个空方法
如果一个类实现了多个接口,这多个接口中又有相同的函数签名的默认方法
 则先使用哪个呢?
    1.类中的方法优先级最高: 类或父类中声明的方法的优先级高于任何声明为默认方法的优先级
    2.选取距离最近
    3.最后 必须显示覆盖和调用期望王法,显式地选择哪一个默认方法的实现


使用Optional取代null
1.optional的创建
 声明一个空的optional Optional.empty()
 依据一个非空值创建Optional  Optional.of(car);
 可接受null的optional Optional.ofNullable(car);

2. 提取转换值
从T中取字段 optional.map(T::getName)

Optional未实现序列化 不要用作类的字段使用
3. 默认行为及解引用Optional对象
    orElse(T) 不存在时设置一个新值
    orElseGet() 是上面的延迟调用版本, Supplier方法只有在Optional对象不含值时才执行调用.如果创建默认值是耗时费力操作,可以使用这种方式, 或者某个方法为空时
    ifPresent(Consumer<T>) 值存在时执行Consumer


Java8日期
LocalDatae 和 LocalTime
特点: 无时区
获取当天 LocalDate.now()
TemporalField是一个接口 定义了如何访问temporal对象某个字段的值
例子: 使用TemporaField读取LocalDate的值 localDate.get(ChronoField.YEAR);
创建指定时间 LocalTime.of(13,45,20) 代表13:45:20
使用字符串创建 LocalDate.parse("2014-03-18")
Duration和Period 就是两个Temporal对象之间的duration
Duration.between(tim1,time2);
如果你需要以年,月,或者日的方式对多个时间建模,可以使用Period类 Period.between(tim1,time)
复杂方法 TemporalAdjuster

猜你喜欢

转载自blog.csdn.net/weixin_42195284/article/details/108426084