Stream API
1 概述
- Stream是java8中处理集合的关键抽象概念
- 它可以指定对集合进行的操作,比如执行肥非常复杂的查找、过滤和映射等操作
- 还可以使用Steam API来进行并行操作
2 Steam实例化
2.1 集合创建
实现自接口collection.stream()
@Test
public void genByCollection() {
var list = new ArrayList<Integer>();
Stream<Integer> stream = list.stream();
}
2.2 数组创建
来自Arrays工具类的stream方法
@Test
public void genByArray() {
var arr = new int[]{1, 2, 3};
IntStream stream = Arrays.stream(arr);
Stream<String> stream2 = Arrays.stream(new String[]{"1", "2"});
var es = new Employee[3];
es[0] = new Employee();
es[1] = new Employee();
es[2] = new Employee();
Stream<Employee> stream1 = Arrays.stream(es);
}
2.3 Stream创建
@Test
public void genByStream() {
Stream<Integer> integerStream = Stream.of(4, 5, 6);
}
2.4 无限流
@Test
public void test() {
// 输出前十个偶数
Stream.iterate(0, t -> t + 2)
// 中间操作
.limit(10)
// 终止操作
.forEach(System.out::println);
// 生成5个随机数
// 提供一个supplier接口
Stream.generate(Math::random)
// 中间操作
.limit(5)
// 终止操作
.forEach(System.out::println);
}
3 Stream中间操作
3.1 常见中间流操作
-
过滤流 filter:
-
截断流 limit:
-
跳跃流 skip:
-
筛选流 distinct:去除重复元素,根据对象的HashCode
流经过终止操作后就会被关闭,不能够再回到中间操作
/**
* 中间操作测试
*/
@Test
public void test1() {
var list = EmployeeData.getEmployees();
// 过滤流
list.stream()
.filter(e -> e.getAge() % 2 == 1)
.forEach(System.out::println);
// 截断流
list.stream()
.limit(4)
.forEach(System.out::println);
// 跳跃流
list.stream()
.skip(3)
.forEach(System.out::println);
list.add(new Employee("1", 1));
list.add(new Employee("1", 1));
list.add(new Employee("1", 1));
// 筛选流
list.stream().distinct()
.forEach(System.out::println);
}
3.2 映射
-
map(Function f)
:接收一个函数作为参数,将流中的每个值都经过该函数处理后转化为一个流,然后嵌套到最外层的流里面var list = Arrays.asList("aa", "bb", "cc"); //aa //bb //cc Stream<Stream<Character>> characterStream = list.stream().map(StreamMapTest::fromStringToStream);
-
flatMap(Function f)
:接收一个函数作为参数,将流中的每个值都转化为一个流,然后把所有的流连接成一个流,类似于对流的一个扁平(flat)处理characterStream.forEach(s -> { s.forEach(System.out::print); System.out.println(); }); Stream<Character> flatStream = list.stream().flatMap(StreamMapTest::fromStringToStream); // aabbcc flatStream.forEach(System.out::print);
-
mapToDouble(Function f)
:接收一个函数作为参数,将流中的每个值都处理转化为一个Double流 -
mapToInt(Function f)
:接收一个函数作为参数,将流中的每个值都处理转化为一个Int流 -
mapToLong(Function f)
:接收一个函数作为参数,将流中的每个值都处理转化为一个Long流
4 终止操作
4.1 match & find 匹配与查找
-
allMatch
:true | false -
anyMatch
:true | false -
noneMatch
:true | false -
count
:返回流的元素的个数 -
findFirst
:返回Optional(防止空指针) -
findAny
:返回Optional(防止空指针) -
max
:返回Optional(防止空指针) -
min
:返回Optional(防止空指针)@Test public void test() { var list = EmployeeData.getEmployees(); var res1 = list.stream().allMatch(e -> e.getAge() > 18); // false System.out.println(res1); var res2 = list.stream().anyMatch(e -> e.getAge() > 18); // false System.out.println(res2); // true var res3 = list.stream().noneMatch(e -> e.getAge() > 18); System.out.println(res3); var res4 = list.stream().count(); // 7 System.out.println(res4); Optional<Employee> first = list.stream().findFirst(); // Optional[Employee(name=0001, age=1)] System.out.println(first); Optional<Employee> any = list.parallelStream().findAny(); // Optional[Employee(name=0005, age=5)] System.out.println(any); // Optional[Employee(name=0007, age=7)] Optional<Employee> max = list.stream().max(Comparator.comparingInt(Employee::getAge)); System.out.println(max); // Optional[Employee(name=0001, age=1)] Optional<Employee> min = list.stream().min(Comparator.comparingInt(Employee::getAge)); System.out.println(min); }
4.2 reduce 规约
-
reduce(T identity, BinaryOperator
accumulator)可以将流中的元素反复结合起来,得到一个值,并返回计算的值加上初始值identity List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); Integer reduce = list.stream().reduce(10, Integer::sum); // 65 System.out.println(reduce);
-
eg:获取全部员工年龄加和
var list2 = EmployeeData.getEmployees(); var sum = list2.stream() .map(Employee::getAge) .reduce(0, Integer::sum); System.out.println(sum);
4.3 收集
-
collect(Collector c)
:将流转化为其他形式,接收一个Collector接口实现,用于给Stream中元素做汇总的方法 -
Collector接口中的方法决定了如何对流执行收集的操作(如收集到List、Set、Map等)
@Test public void test2() { // 查找年龄大于5的员工,并返回为一个list或者set var list = EmployeeData.getEmployees(); var resList = list.stream().filter(e -> e.getAge() > 5) .collect(Collectors.toList()); System.out.println(resList); var resSet = list.stream().filter(e -> e.getAge() > 5) .collect(Collectors.toSet()); System.out.println(resSet); }
-
Collectors的相关方法有:
- toList():把流中的元素收集到list
- toSet():把流中的元素收集到Set
- toCollection():
- counting()::
- summingInt():
- averagingInt():
- summarizingInt():返回值为
IntSummaryStatistics
,收集流中Integer属性的统计值,如平均值
5 Optional类
Optional通过检查空值的方式防止代码污染,避免空指针异常
5.1 Optional的创建
-
Optional.of(T t)
:创建一个Optional实例,t必须非空,否则会报空指针 -
Optional.empty()
:创建一个空Optional -
Optional.ofNullable(T t)
:t可以为空,如果为空返回的是一个value为null的Optional.Empty
常量Girl girl = new Girl(); girl = null; // nullPointerException var optionalGirl = Optional.of(girl);
-
Optional.orElse(T t)
:为空的时候指定返回值,避免了空指针问题public String getGirlName(Boy boy) { var optionalBoy = Optional.ofNullable(boy); // boy1一定非空 var boy1 = optionalBoy.orElse(new Boy(new Girl("girl1"))); var girl = boy1.getGirl(); var optionalGirl = Optional.ofNullable(girl); var girl1 = optionalGirl.orElse(new Girl("girl2")); return girl1.getName(); }
-
Optional.get()
:获取Optional封装的值,如果为空则会抛出异常 -
boolean isPresnt()
:判断是否包含对象 -
void ifPresent(Consumer<? extends T> consumer)
:如果有值,就将该值作为参数就执行Consumer接口的实现代码 -
T orElseGet(Supplier<? extends T> other)
:如果没有值,则执行Supplier接口返回的值作为该值返回 -
T orElseThrow(Supplier<? extends X> eceptionSupplier)
:如果存在值则将值返回,否则抛出supplier接口得到的异常
4 动态代理
-
代理模式的原理:
使用一个代理对象将对象包装起来,然后使用该代理对象包装原始对象,任何对原始对象的调用都要通过代理,代理对象决定是否以及何时将方法调用转到原始对象上
-
静态代理举例:
thread就是一个
代理对象
,t则是被代理对象
,当执行start方法的时候,实际上就是执行的MyThread被代理类
的run方法public class MyThread implements Runnable{ @Override public void run() { } @Test public void test() { MyThread t = new MyThread(); Thread thread = new Thread(t); thread.start(); } }
-
静态代理的缺点:
- 每个代理类只能为一个接口服务,程序开发必然会产生过多的代理
- 代理类和目标类(被代理类)都是在编译期间确定下来,不利于程序的扩展
- 最好能够通过一个代理类实现全部的代理功能——动态代理
-
动态代理指的是客户通过代理类来调用其他对象方法,并且在程序运行时根据需要动态创建目标类的代理对象
-
动态代理需要解决的问题:
- 如何根据加载到内存的被代理类,动态地创建一个代理类及其对象(通过Proxy.newProxyInstance)
- 当通过代理类的对象调用方法a的时候,如何动态地去调用被代理的同名方法a