1. Stream流的创建
/**
* 流是什么?
* 是数据渠道, 用于操作数据源(集合, 数组等) 所生成的元素序列.
* 集合讲的是数据, 流讲的是计算
*
* 注意:
* 1. Stream 自己不会存储元素
* 2. Stream 不会改变源对象. 相反, 他们会返回一个持有结果的新Stream.
* 3. Stream 操作是延迟执行的. 这意味着他们会等需要结果的时候才执行.
*
* 操作:
* 1. 创建Stream : 一个数据源(集合, 数组) , 获取一个流
* 2. 中间操作 : 一个中间操作链对数据源的数据进行处理
* 3. 终止操作 : 一个终止操作, 执行中间操作链, 并产生结果
*
* 获取流:
* 1. Collection系列集合提供的stream() 或 parallelStream()
* List<String> list = new ArrayList<>();
* Stream<String></> stream = list.stream();
*
* 2. 通过Arrays 中的静态方法 stream() 获取数组流
* Employee[] employees = new Employee[10];
* Stream<Employee> stream = Arrays.stream(employees);
*
* 3. 通过Stream类中静态方法 of()
* Stream<String> stream = Stream.of("aa","bb","cc");
*
* 4. 创建无限流
* 1. 迭代 Stream<Integer> stream = Stream.iterator(0,x -> x + 2);
* 2. 生成 Stream<Double> stream = Stream.generate(() -> Math.random());
*/
public class StreamTest {
@Test
public void test(){
Stream<Integer> stream = Stream.iterate(0,x -> x + 2);
stream.limit(10).forEach(System.out::println);
}
}
2. 流的中间操作
2.1. 筛选和切片
/**
* Stream流的中间操作:
* 1. 筛选与切片
* filter : 接受lambda, 从流中排出某些元素.
* limit : 截断流, 使其元素不超过指定数量.
* skip(n) : 跳过元素, 返回一个扔掉了前n个元素的流. 若元素不足n个, 则返回一个空流. 与limit(n)互补.
* distinct : 筛选, 通过流所生成元素的hashCode() 和 equals() 去除重复元素. 对于自定义对象而言, 要重写这两个方法
*
* 注意:
* 由结果可知, 执行过程中出现了迭代, 这是由Stream API产生 -- 内部迭代
* 中间操作不会执行任何操作, 终止操作一次性执行全部内容, 即: 延迟加载(惰性求值)
* 通过短路来提高效率, 找到合适的元素则不会向下执行 类似于 && ||
*/
public class StreamAPITest {
List<Employee> employees = Arrays.asList(
new Employee("张三",18,9000.00),
new Employee("李四",48,6666.00),
new Employee("王五",28,8080.00),
new Employee("赵六",38,5900.00),
new Employee("小张",30,3900.00),
new Employee("老王",40,2900.00),
new Employee("小李",19,1900.00),
new Employee("老李",39,8900.00),
new Employee("老李",39,8900.00),
new Employee("老李",39,8900.00)
);
// 内部迭代: 由Stream API完成
@Test
public void test(){
//employees.stream().filter( employee -> {
//System.out.println("Stream API Filter 操作");
//return employee.getAge() > 30;
//})
//.forEach(System.out::println);
employees.stream().filter( employee -> employee.getAge() > 30)
//.limit(3)
.skip(1)
.distinct()
.forEach(System.out::println);
}
}
2.2. 流的映射
/**
* 映射
* map : 接受Lambda , 将元素转换成其他形式或提取信息. 接收一个函数作为参数,
* 该函数会被应用到每个元素上, 并将其映射成一个新元素.
*
* flatMap : 接收一个函数作为参数, 将流中每一个值都换成另一个流, 然后把所有流连成一个流.
*
* 注意:
* map 方法类似于 List 集合中 add()
* List<String> list = new ArrayList<>(); list.add("aaa"); list.add("bbb");
* List list2 = new ArrayList<>();
* list2.add(111);
* list2.add(list); // [111,[aaa,bbb]]
*
* flatMap 类似于 list 集合中 addAll()方法
* list2.addAll(list); // [111,aaa,bbb]
*/
public class StreamAPITest2 {
List<Employee> employees = Arrays.asList(
new Employee("张三",18,9000.00),
new Employee("李四",48,6666.00),
new Employee("王五",28,8080.00),
new Employee("赵六",38,5900.00),
new Employee("小张",30,3900.00),
new Employee("老王",40,2900.00),
new Employee("小李",19,1900.00),
new Employee("老李",39,8900.00),
new Employee("老李",39,8900.00),
new Employee("老李",39,8900.00)
);
@Test
public void test(){
List<String> list = Arrays.asList("aaa","bbb","ccc","ddd","eee");
list.stream().map(str -> str.toUpperCase()).forEach( str -> System.out.print(str + " "));
//employees.stream().map( employee -> employee.getName()).forEach( str -> System.out.print(str + " "));
System.out.println();
System.out.println("-------------------------------");
// 注意返回结果类型 类似于{{a,a,a},{b,b,b},{c,c,c},{d,d,d}}
Stream<Stream<Character>> streamStream = list.stream().map(StreamAPITest2::filterCharacter);
streamStream.forEach(stream -> {stream.forEach(s -> System.out.print(s + " "));});
System.out.println();
System.out.println("-------------------------------");
// 使用flatMap方法 得到结果类型 类似于 {a,a,a,b,b,b,c,c,c}
Stream<Character> characterStream = list.stream().flatMap(StreamAPITest2::filterCharacter);
characterStream.forEach(s -> System.out.print(s + " "));
}
// 将字符串每个字符存入list集合中
public static Stream<Character> filterCharacter(String str){
List<Character> list = new ArrayList<>();
for(Character ch : str.toCharArray()){
list.add(ch);
}
return list.stream();
}
}
2.3. 排序
/**
* 排序:
* sorted() : 自然排序(Comparable)
*
* sorted(Comparator comparator) : 定制排序
*
*/
public class StreamAPITest3 {
List<Employee> employees = Arrays.asList(
new Employee("张三",18,9000.00),
new Employee("李四",48,6666.00),
new Employee("王五",28,8080.00),
new Employee("赵六",38,5900.00),
new Employee("小张",30,3900.00),
new Employee("老王",40,2900.00),
new Employee("小李",19,1900.00),
new Employee("老李",39,8900.00)
);
@Test
public void test(){
List<String> list = Arrays.asList("ccc","aaa","ddd","bbb");
// 自然排序
list.stream().sorted().forEach(System.out::println);
System.out.println("------------------------------------");
// 定制排序
employees.stream().sorted((e1,e2) -> {
// 先按年龄排序, 再按姓名排序
if(e1.getAge() == e2.getAge()){
return e1.getName().compareTo(e2.getName());
}else {
return Integer.compare(e1.getAge(),e2.getAge());
}
}).forEach(System.out::println);
}
}
3. 终端操作
/**
* 终止操作:
*
* 查找与匹配
* allMatch : 检查是否匹配所有元素 所有都是
* anyMatch : 检查是否至少匹配一个元素 任意一个是
* noneMatch : 检查是否没有匹配所有元素 所有的都不是 跟allMatch相反
* findFirst : 返回第一个元素 返回 Optional<T>类型
* findAny : 返回当前流中任意元素 返回Optional<T> 类型
* count : 返回流中元素个数
* max : 返回流中最大值
* min : 返回流中最小值
*
*/
public class StreamAPITest4 {
List<Employee> employees = Arrays.asList(
new Employee("张三",18,9000.00),
new Employee("李四",48,6666.00),
new Employee("王五",28,8080.00),
new Employee("赵六",38,5900.00),
new Employee("小张",30,13900.00),
new Employee("老王",40,2900.00),
new Employee("小李",19,1900.00),
new Employee("老李",39,8900.00)
);
@Test
public void test(){
// 是否所有人的工资都大于1000元 true
boolean b = employees.stream().allMatch(employee -> employee.getSalary() > 5000d);
System.out.println("是否所有人的工资都超过1000元: " + b); // false
boolean b1 = employees.stream().anyMatch(employee -> employee.getSalary() > 10000d);
System.out.println("是否有人的工资超哥10000元: " + b1); // true
boolean b2 = employees.stream().noneMatch(employee -> employee.getSalary() > 14000d);
System.out.println("是否所有人的工资都不超过14000元: " + b2); // true
boolean b3 = employees.stream().noneMatch(employee -> employee.getSalary() < 1000d);
System.out.println("是否所有人的工资都大于1000元: " + b3); // true
// 按工资排序, 再去第一个元素
Optional<Employee> first = employees.stream().sorted(Comparator.comparingDouble(Employee::getSalary)).findFirst();
System.out.println(first.get());
// 获取工资高于5000的, 随意取出一个
Optional<Employee> any = employees.stream().filter(e -> e.getSalary() > 5000d).findAny();
System.out.println(any.get());
// 获取元素个数
long count = employees.stream().count();
System.out.println("流中元素有: " + count);
// 获取最大值的元素, 指定方式 工资最大的
Optional<Employee> max = employees.stream().max(Comparator.comparingDouble(Employee::getSalary));
System.out.println(max.get());
// 获取最小值得元素, 指定方式 年龄最小的
Optional<Integer> min = employees.stream().map(Employee::getAge).min(Integer::compare);
System.out.println("年龄最小的是: " + min.get());
}
}
4. 归约 收集 分组 分区
/**
* 归约
* reduce(T identify,BinaryOperator)
* reduce(BinaryOperator)
* 可以将流中元素反复结合起来, 得到一个值
*
* 收集
* collect : 将流转换为其他形式. 接收一个Collector接口的实现, 用于给Stream中元素做汇总的方法
* Collector接口中方法的实现决定了如何对流执行收集操作(如收集到 List, Set , Map),
* 但是 Collectors 实用类提供了很多静态方法, 可以方便地创建常见收集器实例.
*
* 分组 / 多级分组
*
* 分区
*
* joining() 用法
*
* 注意:
* map 和 reduce的连接通常称为map - reduce 模式, 因Google用它来进行网络搜索而出名.
*
*/
public class StreamAPITest5 {
List<Employee> employees = Arrays.asList(
new Employee("张三",18,9000.00),
new Employee("李四",48,6666.00),
new Employee("王五",28,8080.00),
new Employee("赵六",38,5900.00),
new Employee("小张",30,13900.00),
new Employee("老王",40,2900.00),
new Employee("小李",19,1900.00),
new Employee("老李",39,8900.00),
new Employee("老李",39,8900.00)
);
@Test
public void test(){
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
// 第一次将 0 赋值给x , 1 赋值给 y , 求和 赋值给x , 在取下一个元素赋值给 y, 以此循环计算
Integer sum = list.stream().reduce(0, (x, y) -> x + y);
System.out.println(sum);
// 求所有员工的工资总和
Double sumSalary = employees.stream().map(Employee::getSalary).reduce(0.0, (x, y) -> x + y);
System.out.println(sumSalary); // 57246.0
// 此种方法无起始值, 有可能为空, 所有用 Optional类型接收
Optional<Double> reduce = employees.stream().map(Employee::getSalary).reduce(Double::sum);
System.out.println(reduce.get()); // 57246.0
// collect收集器 Stream --> List
List<String> collect = employees.stream().map(Employee::getName).collect(Collectors.toList());
System.out.println(collect);
// Stream --> Set 可去重
Set<String> collect1 = employees.stream().map(Employee::getName).collect(Collectors.toSet());
System.out.println(collect1);
// Stream --> 其他特殊集合
HashSet<String> collect2 = employees.stream().map(Employee::getName).collect(Collectors.toCollection(HashSet::new));
System.out.println(collect2);
// 总数
Long collect3 = employees.stream().collect(Collectors.counting());
// 平均值
Double collect4 = employees.stream().collect(Collectors.averagingDouble(Employee::getSalary));
// 总和
DoubleSummaryStatistics collect5 = employees.stream().collect(Collectors.summarizingDouble(Employee::getSalary));
// 最大值
Optional<Employee> max = employees.stream().collect(Collectors.maxBy((e1,e2) -> Double.compare(e1.getSalary(),e2.getSalary())));
System.out.println(max.get());
// 最小值
Optional<Double> min = employees.stream().map(Employee::getSalary).collect(Collectors.minBy(Double::compare));
System.out.println(min.get());
// 分组 根据姓名分组 获得 key - value
Map<String,List<Employee>> map = employees.stream().collect(Collectors.groupingBy(Employee::getName));
// 多级分组
Map<String,Map<String,List<Employee>>> map1 = employees.stream().collect(Collectors.groupingBy(Employee::getName,Collectors.groupingBy((e) -> {
if(((Employee)e).getAge() <= 35){
return "青年";
}else{
return "中年";
}
})));
// 分区 满足条件一个区域 不满足条件一个区域 一个false区, 一个true区
Map<Boolean,List<Employee>> map3 = employees.stream().collect(Collectors.partitioningBy( e -> e.getSalary() > 8000));
// 获取 总数, 平均值, 最大值 另一种方式
DoubleSummaryStatistics dss = employees.stream().collect(Collectors.summarizingDouble(Employee::getSalary));
System.out.println(dss.getAverage());
System.out.println(dss.getCount());
System.out.println(dss.getSum());
System.out.println(dss.getMax());
System.out.println(dss.getMin());
// 获取所有姓名 joining(",","==","===") 还可以加上首尾
String str = employees.stream().map(Employee::getName).collect(Collectors.joining(","));
System.out.println(str);
}
}