什么是Stream?
是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
“集合讲的是数据,流讲的是计算!”
注意:
①Stream 自己不会存储元素。
②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
Stream 的操作三个步骤
⚫ 创建 Stream
一个数据源(如:集合、数组),获取一个流
⚫ 中间操作
一个中间操作链,对数据源的数据进行处理
⚫ 终止操作(终端操作)
一个终止操作,执行中间操作链,并产生结果
/**
* 一、Stream 的操作三个步骤
* 1.创建 Stream
* 一个数据源(如:集合、数组),获取一个流
* 2.中间操作
* 一个中间操作链,对数据源的数据进行处理
* 3.终止操作(终端操作)
* 一个终止操作,执行中间操作链,并产生结果
*/
public class StreamApiTest {
//创建Stream
@Test
public void test1(){
//1.可以通过Collection系列集合提供的stream() 或 parallelStream
List<String> list = new ArrayList<>();
Stream<String> stream1 = list.stream();
//2.通过Arrays中的静态方法Stream()获取数组流
Employee[] employees = new Employee[10];
Stream<Employee> stream2 = Arrays.stream(employees);
//3.通过Stream类中的静态方法of()
Stream<String> stream3 = Stream.of("Hello","world","java");
//4.创建无限流
//①迭代
Stream<Integer> stream4 = Stream.iterate(0,(x) -> x+2);
stream4.limit(10).forEach(System.out::println);
//②生成
Stream.generate(() -> Math.random())
.limit(5)
.forEach(System.out::println);
}
/**
* Stream的中间操作:
* 多个中间操作可以连接起来形成一个流水线,除非流水
* 线上触发终止操作,否则中间操作不会执行任何的处理!
* 而在终止操作时一次性全部处理,称为“惰性求值”。
*
*/
/**
* 筛选与切片
* filter --- 接收Lambda,从流中排除某些元素
* limit --- 截断流,使其元素不超过给定数量
* skip(n) --- 跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流,与limit(n)互补。
* distinct -- 筛选,通过流所生成元素的hashCode() 和 equals() 去除重复元素
*/
List<Employee> employeeList = Arrays.asList(
new Employee("张三",45,999.99),
new Employee("李四",38,666.66),
new Employee("赵六",25,333.33),
new Employee("王五",32,555.55),
new Employee("王五",32,555.55),
new Employee("田七",18,111.11)
);
//内部迭代:迭代操作由Stream API完成
@Test
public void test2(){
//中间操作:不会执行任何操作
Stream<Employee> stream = employeeList.stream()
.filter(e -> {
System.out.println("Stream API 的中间操作");
return e.getAge()>35;
});
//终止操作:一次性执行全部内容,即“惰性求值”
stream.forEach(System.out::println);
}
//外部迭代
@Test
public void test3(){
Iterator<Employee> it = employeeList.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
}
//短路:limit找到符合条件的数量后后续的迭代操作不再进行,即短路,一定程度上提高了效率
@Test
public void test4(){
employeeList.stream()
.filter(e -> {
System.out.println("短路!");
return e.getSalary()>400;
})
.limit(2)
.forEach(System.out::println);
}
/**
* 映射
* map -- 接收 Lambda,将袁术转换成其他啊形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
* flatMap -- 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
*/
@Test
public void test5(){
List<String> list = Arrays.asList("aaa","bbb","ccc","ddd","eee");
list.stream()
.map((str) -> str.toUpperCase())
.forEach(System.out::println);
System.out.println("-----------------");
employeeList.stream()
.map(Employee::getName)
.forEach(System.out::println);
System.out.println("---------------");
Stream<Stream<Character>> stream = list.stream()
.map(StreamApiTest::filterCharacter);//{{a,a,a}{b,b,b}}
stream.forEach(sm -> {
sm.forEach(System.out::println);
});
System.out.println("--------------");
Stream<Character> sm = list.stream()
.flatMap(StreamApiTest::filterCharacter);//{a,a,a,b,b,b}
}
public static Stream<Character> filterCharacter(String str){
List<Character> list = new ArrayList<>();
for (Character ch : str.toCharArray()) {
list.add(ch);
}
return list.stream();
}
//中间操作中的map和flatmap类似于集合中add()和addAll()方法
@Test
public void test6(){
List<String> list = Arrays.asList("aaa","bbb","ccc");
List list2 = new ArrayList();
list2.add(11);
list2.add(22);
// list2.add(list);
// System.out.println(list2);
//[11, 22, [aaa, bbb, ccc]]
list2.addAll(list);
System.out.println(list2);
//[11, 22, aaa, bbb, ccc]
}
/**
* 排序
* sorted() -- 自然排序(Comparable)
* sorted(Comparator com) -- 定制排序(Comparator)
*/
@Test
public void test7(){
List<String> list = Arrays.asList("aaa","bbb","ccc");
list.stream()
.sorted()
.forEach(System.out::println);
// aaa bbb ccc
System.out.println("--------------");
employeeList.stream()
.sorted((e1,e2) -> e1.getName().compareTo(e2.getName()))
.forEach(System.out::println);
// Employee{name='张三', age=45, salary=999.99}
// Employee{name='李四', age=38, salary=666.66}
// Employee{name='王五', age=32, salary=555.55}
// Employee{name='田七', age=18, salary=111.11}
// Employee{name='赵六', age=25, salary=333.33}
}
/**
* 终止操作
*
* allMatch --检查是否匹配所有元素
* anyMatch --检查是否至少匹配一个元素
* noneMatch --检查是否没有匹配所有元素
* findFirst --返回第一个元素
* findAny -- 返回当前流中的任意元素
* count -- 返回流中元素的总个数
* max -- 返回流中最大值
* min -- 返回流中最小值
*/
List<Employee> streamEmployeeList = Arrays.asList(
new Employee("张三",45,999.99, Employee.Status.BUSY),
new Employee("李四",38,666.66,Employee.Status.FREE),
new Employee("赵六",25,333.33, Employee.Status.VOCATION),
new Employee("王五",32,555.55, Employee.Status.BUSY),
new Employee("田七",18,111.11, Employee.Status.VOCATION)
);
@Test
public void test8(){
boolean b1 = streamEmployeeList.stream()
.allMatch(e -> e.getStatus().equals(Employee.Status.BUSY));
//false
boolean b2 = streamEmployeeList.stream()
.anyMatch(e -> e.getStatus().equals(Employee.Status.BUSY));
//true
boolean b3 = streamEmployeeList.stream()
.noneMatch(e -> e.getStatus().equals(Employee.Status.BUSY));
//false
//将返回值放到容器Optional中,是为了防止.findFirst()方法空指针异常,
Optional<Employee> op = streamEmployeeList.stream()
.sorted((e1,e2) -> Double.compare(e1.getSalary(),e2.getSalary()))
.findFirst();
System.out.println(op.get());
Optional<Employee> op2 = streamEmployeeList.stream()
.filter(e -> e.getStatus().equals(Employee.Status.FREE))
.findAny();
System.out.println(op2.get());
}
@Test
public void test9(){
Long count = streamEmployeeList.stream()
.count();
System.out.println(count);//5
Optional<Employee> op = streamEmployeeList.stream()
.max((e1,e2) -> Double.compare(e1.getSalary(),e2.getSalary()));
System.out.println(op.get());
//Employee{name='张三', age=45, salary=999.99}
Optional<Double> op2 = streamEmployeeList.stream()
.map(Employee::getSalary)
.min(Double::compare);
System.out.println(op2.get());
//111.11
}
/**
* 归约
* reduce(T iden, BinaryOperator b) / reduce(BinaryOperator b)
* 可以将流中元素反复结合起来,得到一个值。
*/
@Test
public void test10(){
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Integer sum = list.stream()
.reduce(0,(x,y) -> x+y);
//初始值0赋给x,流中1赋给y
System.out.println(sum);//55
//没有起始值,可能为空,返回optional
Optional<Double> op = employeeList.stream()
.map(Employee::getSalary)
.reduce(Double::sum);
//备注:map 和 reduce 的连接通常称为map-reduce 模式,因 Google 用它
//来进行网络搜索而出名。
System.out.println(op.get());
}
/**
* 收集
* collect--- 将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法
* Collector 接口中方法的实现决定了如何对流执行收集操作(如收
* 集到 List、Set、Map)。但是 Collectors 实用类提供了很多静态
* 方法,可以方便地创建常见收集器实例
*/
@Test
public void test11(){
List<String> list = employeeList.stream()
.map(Employee::getName)
.collect(Collectors.toList());
list.forEach(System.out::println);
System.out.println("----------------");
Set<String> set = employeeList.stream()
.map(Employee::getName)
.collect(Collectors.toSet());
set.forEach(System.out::println);
System.out.println("----------------");
//还可以指定集合
HashSet<String> hs = employeeList.stream()
.map(Employee::getName)
.collect(Collectors.toCollection(HashSet::new));
hs.forEach(System.out::println);
}
@Test
public void test12(){
//总数
Long count = employeeList.stream()
.collect(Collectors.counting());
System.out.println(count);
System.out.println("----------------");
//平均值
Double avg = employeeList.stream()
.collect(Collectors.averagingDouble(Employee::getSalary));
System.out.println(avg);
System.out.println("-----------------");
//总和
Double sum = employeeList.stream()
.collect(Collectors.summingDouble(Employee::getSalary));
System.out.println(sum);
//最大值
Optional<Employee> max = employeeList.stream()
.collect(Collectors.maxBy((e1,e2)->Double.compare(e1.getSalary(),e2.getSalary())));
System.out.println(max.get());
//最小值
Optional<Double> min = employeeList.stream()
.map(Employee::getSalary)
.collect(Collectors.minBy(Double::compare));
System.out.println(max.get());
}
//分组
@Test
public void test13(){
Map<Employee.Status,List<Employee>> map = streamEmployeeList.stream()
.collect(Collectors.groupingBy(Employee::getStatus));
System.out.println(map);
}
//多级分组
@Test
public void test14(){
Map<Employee.Status,Map<String,List<Employee>>> map = streamEmployeeList.stream()
.collect(Collectors.groupingBy(Employee::getStatus,Collectors.groupingBy((e)->{
if (((Employee) e).getAge() <= 35){
return "青年";
}else if (((Employee) e).getAge() <= 50) {
return "中年";
} else {
return "老年";
}
})));
}
//分片/分区--满足条件的一个区,不满足条件的一个区
@Test
public void test15(){
Map<Boolean,List<Employee>> map = employeeList.stream()
.collect(Collectors.partitioningBy((e) -> e.getSalary()>500));
System.out.println(map);
//{false=[Employee{name='赵六', age=25, salary=333.33}, Employee{name='田七', age=18, salary=111.11}],
// true=[Employee{name='张三', age=45, salary=999.99}, Employee{name='李四', age=38, salary=666.66}, Employee{name='王五', age=32, salary=555.55}, Employee{name='王五', age=32, salary=555.55}]}
}
@Test
public void test16(){
DoubleSummaryStatistics dss = employeeList.stream()
.collect(Collectors.summarizingDouble(Employee::getSalary));
System.out.println(dss.getMax());
System.out.println(dss.getAverage());
System.out.println(dss.getSum());
}
//拼接字符串
@Test
public void test17(){
String str = employeeList.stream()
.map(Employee::getName)
.collect(Collectors.joining(","));
System.out.println(str);
//张三,李四,赵六,王五,王五,田七
}
/**
* 练习1
* 给定一个数字列表,返回一个由每个数的平方构成的列表
* 给定[1,2,3,4,5],返回[1,4,9,16,25]
*/
@Test
public void test18(){
Integer[] nums = new Integer[] {1,2,3,4,5};
Arrays.stream(nums)
.map((x) -> x*x)
.forEach(System.out::println);
}
/**
* 练习2
* 怎么用map 和 reduce 方法数一数流中有多少哥Employee呢?
*/
@Test
public void test19(){
Optional<Integer> op = employeeList.stream()
.map((e) -> 1)
.reduce(Integer::sum);
System.out.println(op.get());
//6
}
}