前言:
Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
1、特点:
- 不是数据结构,不会保存数据。
- 不会修改原来的数据源,它会将操作后的数据保存到另外一个对象中。(保留意见:毕竟peek方法可以修改流中元素)
- 惰性求值,流在中间处理过程中,只是对操作进行了记录,并不会立即执行,需要等到执行终止操作的时候才会进行实际的计算。即延迟执行。
2、Stream的操作步骤
3、创建流:(几种方法如下)
/**
* 创建流
*/
@Test
public void test01(){
/**
* 集合流
* - Collection.stream() 穿行流
* - Collection.parallelStream() 并行流
*/
List<String> list = new ArrayList<>();
Stream<String> stream1 = list.stream();
//数组流
//Arrays.stream(array)
String[] strings = new String[10];
Stream<String> stream2 = Arrays.stream(strings);
//Stream 静态方法
//Stream.of(...)
Stream<Integer> stream3 = Stream.of(1, 2, 3);
//无限流
//迭代
Stream<Integer> stream4 = Stream.iterate(0, (i) -> ++i+i++);
stream4.forEach(System.out::println);
//生成
Stream.generate(() -> Math.random())
.limit(5)
.forEach(System.out::println);
}
4、筛选 / 切片
中间操作:
- filter:接收 Lambda ,从流中排除某些元素
- limit:截断流,使其元素不超过给定数量
- skip(n):跳过元素,返回一个舍弃了前n个元素的流;若流中元素不足n个,则返回一个空流;与 limit(n) 互补
- distinct:筛选,通过流所生成的 hashCode() 与 equals() 取除重复元素
List<Employee> emps = Arrays.asList(
new Employee(101, "Z3", 19, 9999.99),
new Employee(102, "L4", 20, 7777.77),
new Employee(103, "W5", 35, 6666.66),
new Employee(104, "Tom", 44, 1111.11),
new Employee(105, "Jerry", 60, 4444.44)
);
@Test
public void test01(){
emps.stream()
.filter((x) -> x.getAge() > 35)
.limit(3) //短路?达到满足不再内部迭代
.distinct()
.skip(1)
.forEach(System.out::println);
}
小知识:
内部迭代:迭代操作由 Stream API 完成
外部迭代:我们通过迭代器完成
5、映射
- map:接收 Lambda ,将元素转换为其他形式或提取信息;接受一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
- flatMap:接收一个函数作为参数,将流中每一个值都换成另一个流,然后把所有流重新连接成一个流
map:
@Test
public void test02(){
List<String> list = Arrays.asList("a", "b", "c");
list.stream()
.map((str) -> str.toUpperCase())
.forEach(System.out::println);
}
flatMap:
public Stream<Character> filterCharacter(String str){
List<Character> list = new ArrayList<>();
for (char c : str.toCharArray()) {
list.add(c);
}
return list.stream();
}
@Test
public void test03(){
List<String> list = Arrays.asList("a", "b", "c");
Test02 test02 = new Test02();
list.stream()
.flatMap(test02::filterCharacter)
.forEach(System.out::println);
}
6、排序
- sorted():自然排序
- sorted(Comparator c):定制排序
Comparable:自然排序
@Test
public void test04(){
List<Integer> list = Arrays.asList(1,2,3,4,5);
list.stream()
.sorted() //comparaTo()
.forEach(System.out::println);
}
Comparator:定制排序
@Test
public void test05(){
emps.stream()
.sorted((e1, e2) -> { //compara()
if (e1.getAge().equals(e2.getAge())){
return e1.getName().compareTo(e2.getName());
} else {
return e1.getAge().compareTo(e2.getAge());
}
})
.forEach(System.out::println);
}
7、查找 / 匹配
终止操作:
- allMatch:检查是否匹配所有元素
- anyMatch:检查是否至少匹配一个元素
- noneMatch:检查是否没有匹配所有元素
- findFirst:返回第一个元素
- findAny:返回当前流中的任意元素
- count:返回流中元素的总个数
- max:返回流中最大值
- min:返回流中最小值
public enum Status {
FREE, BUSY, VOCATION;
}
@Test
public void test01(){
List<Status> list = Arrays.asList(Status.FREE, Status.BUSY, Status.VOCATION);
boolean flag1 = list.stream()
.allMatch((s) -> s.equals(Status.BUSY));
System.out.println(flag1);
boolean flag2 = list.stream()
.anyMatch((s) -> s.equals(Status.BUSY));
System.out.println(flag2);
boolean flag3 = list.stream()
.noneMatch((s) -> s.equals(Status.BUSY));
System.out.println(flag3);
// 避免空指针异常
Optional<Status> op1 = list.stream()
.findFirst();
// 如果Optional为空 找一个替代的对象
Status s1 = op1.orElse(Status.BUSY);
System.out.println(s1);
Optional<Status> op2 = list.stream()
.findAny();
System.out.println(op2);
long count = list.stream()
.count();
System.out.println(count);
}
8、归约 / 收集
- 归约:reduce(T identity, BinaryOperator) / reduce(BinaryOperator) 可以将流中的数据反复结合起来,得到一个值
- 收集:collect 将流转换成其他形式;接收一个 Collector 接口的实现,用于给流中元素做汇总的方法
reduce:
/**
* Java:
* - reduce:需提供默认值(初始值)
* Kotlin:
* - fold:不需要默认值(初始值)
* - reduce:需提供默认值(初始值)
*/
@Test
public void test01(){
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
Integer integer = list.stream()
.reduce(0, (x, y) -> x + y);
System.out.println(integer);
}
collect:
List<Employee> emps = Arrays.asList(
new Employee(101, "Z3", 19, 9999.99),
new Employee(102, "L4", 20, 7777.77),
new Employee(103, "W5", 35, 6666.66),
new Employee(104, "Tom", 44, 1111.11),
new Employee(105, "Jerry", 60, 4444.44)
);
@Test
public void test02(){
//放入List
List<String> list = emps.stream()
.map(Employee::getName)
.collect(Collectors.toList());
list.forEach(System.out::println);
//放入Set
Set<String> set = emps.stream()
.map(Employee::getName)
.collect(Collectors.toSet());
set.forEach(System.out::println);
//放入LinkedHashSet
LinkedHashSet<String> linkedHashSet = emps.stream()
.map(Employee::getName)
.collect(Collectors.toCollection(LinkedHashSet::new));
linkedHashSet.forEach(System.out::println);
}
@Test
public void test03(){
//总数
Long count = emps.stream()
.collect(Collectors.counting());
System.out.println(count);
//平均值
Double avg = emps.stream()
.collect(Collectors.averagingDouble(Employee::getSalary));
System.out.println(avg);
//总和
Double sum = emps.stream()
.collect(Collectors.summingDouble(Employee::getSalary));
System.out.println(sum);
//最大值
Optional<Employee> max = emps.stream()
.collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
System.out.println(max.get());
//最小值
Optional<Double> min = emps.stream()
.map(Employee::getSalary)
.collect(Collectors.minBy(Double::compare));
System.out.println(min.get());
}
@Test
public void test04(){
//分组
Map<Integer, List<Employee>> map = emps.stream()
.collect(Collectors.groupingBy(Employee::getId));
System.out.println(map);
//多级分组
Map<Integer, Map<String, List<Employee>>> mapMap = emps.stream()
.collect(Collectors.groupingBy(Employee::getId, Collectors.groupingBy((e) -> {
if (e.getAge() > 35) {
return "开除";
} else {
return "继续加班";
}
})));
System.out.println(mapMap);
//分区
Map<Boolean, List<Employee>> listMap = emps.stream()
.collect(Collectors.partitioningBy((e) -> e.getSalary() > 4321));
System.out.println(listMap);
}
@Test
public void test05(){
//总结
DoubleSummaryStatistics dss = emps.stream()
.collect(Collectors.summarizingDouble(Employee::getSalary));
System.out.println(dss.getMax());
System.out.println(dss.getMin());
System.out.println(dss.getSum());
System.out.println(dss.getCount());
System.out.println(dss.getAverage());
//连接
String str = emps.stream()
.map(Employee::getName)
.collect(Collectors.joining("-")); //可传入分隔符
System.out.println(str);
}
9、案例
案例一: 给定一个数字列表,如何返回一个由每个数的平方构成的列表呢?(如:给定【1,2,3,4,5】,返回【1,4,9,16,25】)
@Test
public void test01(){
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
list.stream()
.map((x) -> x * x)
.forEach(System.out::println);
}
案例二: 怎样使用 map 和 reduce 数一数流中有多少个 Employee 呢?
List<Employee> emps = Arrays.asList(
new Employee(101, "Z3", 19, 9999.99),
new Employee(102, "L4", 20, 7777.77),
new Employee(103, "W5", 35, 6666.66),
new Employee(104, "Tom", 44, 1111.11),
new Employee(105, "Jerry", 60, 4444.44)
);
@Test
public void test02(){
Optional<Integer> result = emps.stream()
.map((e) -> 1)
.reduce(Integer::sum);
System.out.println(result.get());
10、并行流
- 并行流:就是把一个内容分成几个数据块,并用不同的线程分别处理每个数据块的流
- Java 8 中将并行进行了优化,我们可以很容易的对数据进行操作;Stream API 可以声明性地通过 parallel() 与 sequential() 在并行流与串行流之间切换
Fork / Join 框架:
Fork / Join 框架与传统线程池的区别:
Fork / Join 实现:
public class ForkJoinCalculate extends RecursiveTask<Long> {
private static final long serialVersionUID = 1234567890L;
private long start;
private long end;
private static final long THRESHPLD = 10000;
public ForkJoinCalculate(long start, long end) {
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
long length = end - start;
if (length <= THRESHPLD) {
long sum = 0;
for (long i = start; i <= end; i++) {
sum += i;
}
} else {
long middle = (start + end) / 2;
ForkJoinCalculate left = new ForkJoinCalculate(start, end);
left.fork(); //拆分子任务 压入线程队列
ForkJoinCalculate right = new ForkJoinCalculate(middle + 1, end);
right.fork();
return left.join() + right.join();
}
return null;
}
}
public class TestForkJoin {
/**
* ForkJoin 框架
*/
@Test
public void test01(){
Instant start = Instant.now();
ForkJoinPool pool = new ForkJoinPool();
ForkJoinCalculate task = new ForkJoinCalculate(0, 100000000L);
Long sum = pool.invoke(task);
System.out.println(sum);
Instant end = Instant.now();
System.out.println(Duration.between(start, end).getNano());
}
/**
* 普通 for循环
*/
@Test
public void test02(){
Instant start = Instant.now();
Long sum = 0L;
for (long i = 0; i < 100000000L; i++) {
sum += i;
}
Instant end = Instant.now();
System.out.println(Duration.between(start, end).getNano());
}
}
Java 8 并行流 / 串行流:
@Test
public void test03(){
//串行流(单线程):切换为并行流 parallel()
//并行流:切换为串行流 sequential()
LongStream.rangeClosed(0, 100000000L)
.parallel() //底层:ForkJoin
.reduce(0, Long::sum);
}
11、Optional
定义: Optional 类 (java.util.Optional) 是一个容器类,代表一个值存在或不存在,原来用 null 表示一个值不存在,现在用 Optional 可以更好的表达这个概念;并且可以避免空指针异常。
常用方法:
- Optional.of(T t):创建一个 Optional 实例
- Optional.empty(T t):创建一个空的 Optional 实例
- Optional.ofNullable(T t):若 t 不为 null,创建 Optional 实例,否则空实例
- isPresent():判断是否包含某值
- orElse(T t):如果调用对象包含值,返回该值,否则返回 t
- orElseGet(Supplier s):如果调用对象包含值,返回该值,否则返回 s 获取的值
- map(Function f):如果有值对其处理,并返回处理后的 Optional,否则返回 Optional.empty()
- flatmap(Function mapper):与 map 相似,要求返回值必须是 Optional
Optional.of(T t):
@Test
public void test01(){
Optional<Employee> op = Optional.of(new Employee());
Employee employee = op.get();
}
Optional.empty(T t):
@Test
public void test02(){
Optional<Employee> op = Optional.empty();
Employee employee = op.get();
}
Optional.ofNullable(T t):
@Test
public void test03(){
Optional<Employee> op = Optional.ofNullable(new Employee());
Employee employee = op.get();
}
isPresent():
@Test
public void test03(){
Optional<Employee> op = Optional.ofNullable(new Employee());
if (op.isPresent()) {
Employee employee = op.get();
}
}