本文最后更新于 1085 天前,其中的信息可能已经有所发展或是发生改变。
之前看项目代码时经常发现操作集合时用到了stream,但是看不太懂,于是决定写一篇博客作为学习笔记以便日后查验
概念
Stream是JDK8 API引入的新特性,用于对集合、数组进行复杂的查找、过滤、映射数据等操作。 工作原理: 把数据源(集合、数组等)转化成stream流并且进行一系列中间操作,最后产生一个新流。
- 特点
1、stream自己不会存储元素 2、stream不会改变源对象 3、stream操作是惰性的,即需要结果时才执行
- 操作步骤
(1)获取stream流 通过一个数据源获取一个流 (2)中间操作 一个中间操作链,对数据源的流进行处理 (3)终止操作 一个终止操作,执行中间操作链,并产生结果
获取stream流
- 通过Collection系列集合提供的stream()或parallelstream()
List<String> list = new ArrayList();
Stream<string> stream1= list.stream();
//stream()获取的是串行流
//parallelstream()获取的是并行流
- 通过Arrays中的静态方法stream()获取数组流
int[] a = new int[];
Stream<int> stream2 = Array.stream(a);
- 通过Stream类中的静态方法of()
Stream<String> stream3 = Stream.of("aa","bb","cc");
- 创建无限流
//迭代
Steam<Integer> stream4 = Stream.iterate(0, (x) -> x + 2);
//生成
Stream.generate(() -> Math.random());
中间操作
多个中间操作可以连接起来形成流水线(操作的返回值仍然是Stream),除非流水线上出发终止操作,否则中间操作不会执行任何处理,而在终止操作时一次性全部处理完,称为“惰性求值”
筛选与切片
filter(lambda): 接受lambda函数,根据函数从流中排除某些元素 limit(num): 截断流,使其元素不超过num skip(num): 跳过num个元素。若流中元素不足n个,则返回一个空流。 与limit互补。 distinct(): 筛选,通过流所生成元素的hashCode()和equals()去除重复元素
- 示例
employees集合:(pojo省略)
List<Employee> employees = Arrays.asList(
new Employee("张三",18,9999.99),
new Employee("李四",58,5555.55),
new Employee("王五",26,3333.33),
new Employee("赵六",36,6666.66),
new Employee("田七",12,8888.88)
)
//Array.asList():将数组转化为List
Stream:
employees.stream()
.filter((x) -> x.getAge() > 12)
.limit(2)
.distinct()
.skip(1)
.forEach(System.out::println);
/*
结果:
李四 58 5555.55
王五 26 3333.33
*/
System.out::println是java8的一种新特性:方法引用
本质是对lambda表达式的进一步简化
这里等价于(str)->System.out.println(str)
映射
map(lambda): 接收lambda函数,该函数会被用到每个元素上,将其映射成一个新的元素。 flatMap(lambda): 接收lambda函数,将流中的每个值都转换成另一个流,再把所有的流连接成一个流 Map和flatMap的区别:flatMap是对流进行扁平化处理 (我对这个flatMap的理解是拆分+连接,Map是无拆分+连接;map()类似于List的add(),flatMap类似于List的addAll()) (初见扁“扁平化处理”这个词时感觉很怪,但是看了一些实例、了解了这两者的区别后,发现这个词用的真的太妙了)
- map:
Arrays.asList("a","b","c").stream()
.map((str)->str.toUpperCase())
.forEach(System.out::println);
/*
结果:
A
B
C
*/
- flatMap:
Arrays.asList("aaa", "bbb", "ccc").stream()
.flatMap((str) -> {
List<Character> list = new ArrayList<>();
for (char c : str.toCharArray()) {
list.add(c);
}
return list.stream();
}
)
.forEach(System.out::println);
/*
结果:
a
a
a
b
b
b
c
c
c
*/
排序
sorted(): 自然排序 按照字典序排序 sorted(Comparator com): 定制排序
- 示例
employees集合:(pojo省略)
List<Employee> employees = Arrays.asList(
new Employee("小红", 13, 5),
new Employee("小明", 13, 7),
new Employee("小王", 15, 3),
new Employee("小白", 14, 7),
new Employee("小刚", 12, 8)
);
//Array.asList():将数组转化为List
定制排序:
employees.stream()
.sorted((e1, e2) -> { //compara()
if (e1.getAge() == (e2.getAge())) {
return e1.getSalary() > e2.getSalary() ? 1 : -1;
} else {
return e1.getAge() > e2.getAge() ? 1 : -1;
}
})
.forEach(System.out::println);
/*
结果:
小刚 12 8.0
小红 13 5.0
小明 13 7.0
小白 14 7.0
小王 15 3.0
*/
终止操作
查找与匹配
boolean allMatch(lambda): 检测是否匹配所有元素 boolean anyMatch(lambda): 检测是否至少匹配一个元素 boolean noneMatch(lambda): 检测是否所有元素都不匹配 Optional<T> findFirst(): 返回第一个元素 Optional<T> findAny(): 返回任意一个元素 long count(): 返回元素个数 Optional<T> max(): 返回最大值 Optional<T> min(): 返回最小值 forEach(lambda) 内部迭代
规约/收集
归约: T reduce(T identity, BinaryOperator) / Optional<T> reduce(BinaryOperator) 可以将流中的数据反复结合起来,得到一个值 前者返回T,后者返回Optional<T>(表示T可能为null) 收集: collect(collector) 将流转换成其他形式;接收一个 Collector 接口的实现,用于给流中元素做汇总的方法 Collectors实用类提供了很多静态方法用于创建收集器实例
- 规约
//案例1:求1-10的和
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);
System.out.println(sum);
//案例2:求公司员工工资总和
Optional<Double> op = employee.stream()
.map(EMployee::getSalary)
.reduce(Double::sum);
double sum = op.get();
System.out.println(op.get());
/*
注意:
案例1中reduce()的返回值是Integer
案例2中reduce()的返回值是Optional<T>
这是因为案例1中reduce()的返回值不可能为null,因为已经传入了一个"0"
*/
- 收集
//案例1:获取一个包含公司所有员工名的List
List<string> list = employees.stream()
.map(Employee::getName)
.collect(Collectors.toList());
list.forEach(System.out::println);
/*
当需要一个特殊的集合时(比如HashSet),可以这样写:
HashSet<string> hashSet = employees.stream()
.map(Employee::getName)
.collect(Collectors.toCollection(HashSet::new));
hashSet.forEach(System.out::println);
*/
//案例2:返回员工年龄总数
Long count = employees.stream()
.collect(Collection.counting());
/*
等价于
Long count = employees.stream()
.map(Employee::getAge())
.count();
*/
//案例3:对员工按照状态分组(分组)
Map<Status, List<Employee>> map = employees.stream()
.collect(Collectors.groupingBy(Employee::getStatus))
//案例4:对员工先按照状态分组,后按照年龄分组(多级分组)
Map<Status, Map<String, List<Employee>>> map = employee.stream()
.collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy((e) -> {
return e.getAge() < 35 ? "青年" : "壮年";
})));
//案例5:对员工工资是否达到8000进行分类(分区)
Map<Boolean, List<Employee>> map = employees.stream()
.collect(collectors.partitioningBy((e)->e.getSalary()>8000));
//案例6:对员工的工资进行各种操作(操作)
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());
//案例7:连接所有员工的姓名作为一个字符串(连接字符串)
String str = emps.stream()
.map(Employee::getName)
.collect(Collectors.joining("-")); //可传入分隔符
System.out.println(str);