OnJava05--集合、函数式编程、流
OnJava05--集合、函数式编程、流
1、集合
Java集合类库是用来“持有”对象的,有两个基本接口:
- Collection:由单独元素组成的序列
- Map:一组键值对象对,用键来查找值
小记
- 队列和栈的行为都是通过LinkedList提供的
- 栈:Java 1.0开始提供的Stack类设计很糟糕(继承了Vector ),推荐使用ArrayDeque代替:
1 |
|
Queue 队列:典型的先进先出集合,放入顺序和取出顺序一样。
LinkedList实现了Queue 接口,所以可以将其作为Queue的一种实现来使用:
1
Queue<Integer> queue = new LinkedList<>();
PriorityQueue:优先级队列,默认的排序方法是对象在队列中的自然排序,因此默认是小根堆
2、函数式编程
面向对象编程抽象数据,函数式编程抽象行为。
2.1 lambda表达式
lambda表达式产生的是函数,而不是类
基本语法
参数;
后面跟->,可以理解为“产生”
->后面的都是方法体
2.2 方法引用
2.3 函数式接口
Java8引入了包含一组接口的java.util.function,其中的每个接口都只包含一个抽象方法,叫做函数式方法。
注解@FunctionalInterface:强制实施
- 如果使用了该注解的接口中方法多于一个,编译会报错“Multiple non-overriding abstract methods found in interface xxx“
- 使用了该注解的接口也叫单一抽象方法类型
函数式接口的基本类型变种 存在的原因:
- 防止在传递参数和返回结果时涉及自动装箱和自动拆箱 -- 为了性能
- eg:
1
2Function<Integer, Double> fid = i -> (double)i; // 使用基础的Function,必须进行强转
IntToDoubleFunction fid2 = i -> i; // IntToDoubleFunction不需要强转
2.4 高阶函数
是一个能接受函数作为参数或返回值的函数。
2.5 闭包
1 |
|
上述代码段1编译会失败,提示:“Variable used in lambda expression should be final or effectively final”
而下面的代码段2可以编译:
1 |
|
- 代码段2中,x和i并不是final的变量,却可以编译成功。Effectively
final(“实际上的最终变量”)意义就出现在这里,这个术语是为Java
8创建的,意思是虽然未显式地将一个变量声明为最终变量,但仍然可以用最终变量的方式对待它。如果一个局部变量的初始值从不改变,就是Effectively
final。
- 所谓Effectively final,意味着可以在变量声明前加上final关键字,而不用修改其余代码。
- 对于对象的引用,只要不重新指向其他对象就仍可以是Effectively final,可以修改对象本身。
2.6 函数组合
将多个函数结合使用,以创建新的函数。
- func.andThen(after):先执行func,再执行after
- func.compose(before):先执行before,再执行func
- pred.and(other):短路与,pred && other
- pred.or(other):短路或,pred || other
- pred.negate():pred取反
3、流
3.1 流的重要特性
- 内部迭代
- 外部迭代:显式编写(for、while等)
- 内部迭代:流编程的一个核心特性,优势:
- 可读性更好
- 更易利用多处理器:并行化机制会自动控制
- 惰性求值:流只在绝对必要时才被求值,可以想象成“延迟列表”
- 因此,流可以表示非常大甚至无限大的序列,而不用考虑内存问题
3.2 流的创建
(1)Stream.of(T... values):将一组条目变成一个流
(2)每个Collection都有stream()方法,生成一个流
(3)随机数流:
- Random类生成的是基本类型,boxed()流操作会自动装箱
- ints、longs、doubles都有多套方法签名(无参、限制上下限、限制流大小、同时限制流大小和边界),见代码示例
1 |
|
(4)Stream.generate(Supplier
(5)Stream.iterate(final T seed, final UnaryOperator
(6)Stream.builder,流生成器,代码示例:一旦build()就不能再往里add()
1 |
|
(7)Arrays.stream(T[] array) 将数组转为流
1 |
|
(8)正则表达式,splitAsStream:
1 |
|
3.3 中间操作
中间操作从一个流中接收对象,并将对象作为另一个流送出后端,连接到其他操作。
(1)peek():跟踪与调试,接收遵循Consumer函数式接口的函数,没有返回值,所以只能“看看”对象
(2)sorted():排序,可以按需指定比较器
1 |
|
(3)移除元素:
- distinct():移除重复元素
- filter(Predicate pred):保留pred为true的元素
(4)映射
- map(Function)
- mapToInt(ToIntFunction)
- mapToLong(ToLongFunction)
- mapToDouble(ToDoubleFunction)
(5)flatMap():扁平化,将元素流的流里的每个元素流展开为元素(扁平化),传出元素流
3.4 Optional类型
3.4.1 基本概念
(1)既可以作为流元素来占位,又可以保障不存在要找的元素时不会抛出异常。
(2)以下流操作会返回Optional对象,因为无法确保它们的结果一定存在:
- findFirst
- findAny
- max、min
- reduce
- average
当流为空时,上述操作的结果均不存在,会返回Optional.empty。
(3)PS:创建空流的语法:
1 |
|
(4)Optional的两个基本动作:
- isPresent:判断里面是否有东西
- get:如果有东西,获取它
3.4.2 便捷函数
简化了“先检查再处理所包含的对象”的过程
ifPresent:如果值存在,就用这个值调用consumer,否则什么都不做
1
void ifPresent(Consumer<? super T> consumer)
orElse:如果对象存在,则返回这个对象,否则返回other
1
T orElse(T other)
orElseGet:如果对象存在,则返回这个对象,否则返回other函数创建的对象
1
T orElseGet(Supplier<? extends T> other)
orElseThrow:否则抛出一个exceptionSupplier创建的异常
1
<X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X
3.4.3 创建Optional
Optional类中有下面三个静态方法,来创建Optional:
- empty():空的Optional
- of(value):已知value非空
- ofNullable(value):不确定value是不是null
- value是null时,自动返回Optional.empty
- 否则将value包在Optional中
3.4.4 Optional上的filter
- 普通的流filter:如果pred返回false,会从流中删除元素
- Optional.filter:如果pred返回false,不会删除元素,而是转化为empty
3.4.5 由Optional组成的流
需要针对不同应用采取不同的方法,见代码示例:
1 |
|
3.5 终结操作
接受一个流,并生成一个最终结果。
(1)将流转换为数组:
- toArray()
(2)在每个元素上应用某个终结操作:
- forEach:
1 |
|
1 |
|
(3)收集操作:
- collect(Collector):若java.util.stream.Collectors里没有想要的集合类型,可以使用Collectors.toCollection,并将任何类型的Collection的构造方法引用传递给它:
1 |
|
(4)组合所有的流元素: - reduce: 1
2
3Optional<T> reduce(BinaryOperator<T> accumulator)
T reduce(T identity, BinaryOperator<T> accumulator);
区别在于下面的方法会将identity作为初值,即使流是空的,也能返回identity。
(5)匹配,下面的api都是短路计算,即遇到一个不符合的元素时,立即返回false
allMatch:
1
boolean allMatch(Predicate<? super T> predicate); // 流中所有元素,predicate都为true
anyMatch:
1
boolean anyMatch(Predicate<? super T> predicate); // 流中存在任一元素,predicate为true
noneMatch:
1
boolean noneMatch(Predicate<? super T> predicate); // 流中所有元素,predicate都为false
(6)选择一个元素
findFirst:
1
Optional<T> findFirst();
findAny:
1
Optional<T> findAny();
如果需要选择流的最后一个元素,需要使用reduce:
1 |
|
(7)获得流的相关信息
count:
1
long count(); // 流中元素个数
max:
1
Optional<T> max(Comparator<? super T> comparator); // 最大元素
min:
1
Optional<T> min(Comparator<? super T> comparator); // 最小元素
数值类型的流还可以计算均值、求和等:
1 |
|
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!