只是笔记 笔记 不是教程
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