- 流式编程式微了方便处理集合类数据,节省代码量,包括 集合的过滤,排序,映射等功能,
- 根据流的操作性分为串行流,并行流
-
什么是流
不是一个数据结构,只是一个高级的迭代或者遍历
-
常用流操作
流主要针对集合相关的操作,所有继承了 collection接口的都可以使用流
例程
Integer[] nums = new Integer[]{1, 2, 3, 4, 56};
List<Integer> evens = new ArrayList<>();
for (
final Integer num : nums) {
if (num % 2 == 0) {
evens.add(num);
}
}
如上采用传统的操作对一个数组进行处理并返回符合要求的数组需要多行代码。接下来我们用流式来搞一遍
Stream<Integer> stream = Arrays.stream(nums);
List<Integer> collect = stream.filter(num -> num % 2 == 0).collect(Collectors.toList());
解析
- 首先通过arrays.stream(nums) 转换成流对象 之后调用流式处理的方法
- 其中使用到了 中间操作的 filter方法
案例实践
// 什么是中间操作
// 初始化测试用数据
// 初始化
List<Student> students = new ArrayList<Student>() {
{
add(new Student(20160001, "孔明", 20, 1, "土木工程", "武汉大学"));
add(new Student(20160002, "伯约", 21, 2, "信息安全", "武汉大学"));
add(new Student(20160003, "玄德", 22, 3, "经济管理", "武汉大学"));
add(new Student(20160004, "云长", 21, 2, "信息安全", "武汉大学"));
add(new Student(20161001, "翼德", 21, 2, "机械与自动化", "华中科技大学"));
add(new Student(20161002, "元直", 23, 4, "土木工程", "华中科技大学"));
add(new Student(20161003, "奉孝", 23, 4, "计算机科学", "华中科技大学"));
add(new Student(20162001, "仲谋", 22, 3, "土木工程", "浙江大学"));
add(new Student(20162002, "鲁肃", 23, 4, "计算机科学", "浙江大学"));
add(new Student(20163001, "丁奉", 24, 5, "土木工程", "南京大学"));
}
};
filter 过滤
```java // 在前面的例子中我们已经演示了如何使用filter,其定义为:Streamfilter(Predicate<? super T> predicate), // filter接受一个谓词Predicate,我们可以通过这个谓词定义筛选条件,在介绍lambda表达式时我们介绍过Predicate是一个函数式接口 List whuStudents = students.stream()
for (Student s1 :// 谓词可以理解为集合中的一个元素形参指代 然后直接调用方法去编写即可 filter中编写过滤的方法,
// 可以是比较,判断,计算
.filter(student -> "武汉大学".equals(student.getSchool()))
// 重新收集成为一个studnet arrayList
.collect(Collectors.toList());
}whuStudents) { System.out.println(s1.getSchool() +" 姓名:" + s1.getName());
<a name="xsuk3"></a>
# distinct 去重
- 类似sql中的distinct,对集合数据进行去重复,但是这个distinct方法是根据hashcode和equals方法来进行去重,显然,当面对对象列表的时候,这个方法要实现起来就需要去重写实体类的hashcode和equals方法。
- 方案一、
```java
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Predicate;
import java.util.function.Function;
public static <T> Predicate <T> distinctByKey(Function<? super T, Object> keyExtractor) {
Map<Object, Boolean> seen = new ConcurrentHashMap<>();
System.out.println("这个函数将应用到每一个item");
return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}
List<Student> collect1 = students.stream().filter(distinctByKey(b->b.getName())).collect(Collectors.toList());
- 方案2
- 使用衍生功能进行去重
//long num = addList.stream().distinct().count(); long count = addList.stream().collect(Collectors.collectingAndThen( Collectors.toCollection(() -> new TreeSet<>( Comparator.comparing(po ->DateTimeFormatter.ofPattern("yyyy-MM-dd").format(po.getOrderTime()) + ";" + po.getCityCode()))), ArrayList::new)).stream().count(); Assert.checkBoolean(addList.size() == count, "Excel中,存在相同日期的城市活跃度"); # 上述案例未经测试
limit
// limit 操作也类似sql语句中的limit关键字,不过相对功能较弱,limit返回包含前n个元素的流,当集合大小小于n时,则返回实际长度,比如下面的例子返回前两个专业为土木工程专业的学生 List<Student> collect2 = students.stream().filter(student -> "土木工程".equals(student.getMajor())).limit(5).collect(Collectors.toList()); System.out.println(collect2.size()); // 还有另外一个流操作,sorted,该操作用于对流中元素进行排序,sorted要求待比较的元素必须实现comparable接口,如果没有实现也不要紧,可以将比较器作为参数传递给sorted(Comparator List<Student> sortedCivilStudents = students.stream() .filter(student -> "土木工程".equals(student.getMajor())).sorted((s1, s2) -> s1.getAge() - s2.getAge()) .limit(2) .collect(Collectors.toList()); System.out.println(sortedCivilStudents);
skip
```java // skip // skip 操作与limit操作相反,如同其字面意思一样,是跳过前n个元素,比如我们希望找出排序在2之后的土木工程专业的学生,可以写为 ListcivilStudents = students.stream() .filter(student -> "土木工程".equals(student.getMajor())) .skip(2) .collect(Collectors.toList());
- 使用衍生功能进行去重
<a name="AJ3Sx"></a>
# 映射处理
- _在sql中,借助select 关键字后面添加需要的字段名称,可以仅输出需要的字段数据,而流式处理的映射操作也是实现这一目的,在jdk8的流式处理中,主要包含两类映射操作,map,和flatMap
_
<a name="OiK7H"></a>
## map
```java
// // map
// // 例子
// /*
// 需求背景 : 假设希望筛选出所有专业为计算机科学的学生姓名,那么可以在filter筛选的基础上,通过map将学生实体映射为学生姓名字符串
//
// */
List<String> collect3 = students.stream().
filter(student -> "计算机科学".equals(student.getMajor())).map(Student::getName).collect(Collectors.toList());
System.out.println(collect3);
//// 除了上面这类基础的map,java8还提供了mapToDouble(ToDoubleFunction<? super T> mapper),mapToInt(ToIntFunction<? super T> mapper),mapToLong(ToLongFunction<? super T> mapper),
//// 这些映射分别返回对应类型的流,java8为这些流设定了一些特殊的操作,比如我们希望计算所有专业为计算机科学学生的年龄之和,那么我们可以实现如下:
//
long sum = students.stream().
filter(student -> "计算机科学".equals(student.getMajor())).
mapToInt(Student::getAge).sum();
System.out.println(sum);
flatmap
// flatMap
// // flatMap与map的区别在于 flatMap是将一个流中的每个值都转换成一个个流,然后再将这些流扁平化称为一个流,距离说明,假设我们有一个字符串数组
// // String[] strs= {'java8',"is","easy","to","learn"} 希望输出构成这一组数组的所有非重复自负,可能首先会想到
String[] strs = {"jdk8", "is", "easy", "to", "learn"};
List<String[]> distinctStrs = Arrays.stream(strs)
.map(str -> str.split("")) // 映射成为Stream<String[]>
.distinct()
.collect(Collectors.toList());
System.out.println(distinctStrs.toString());
// // 但是执行之后得到的结果是一个包含多个字符串, 构成一个字符串的字符数组 的流,此时执行distinct操作是给予在这些字符串数组之间的对比,所以达不到目的
//
// // dinstinct 还有对一个包含多个字符的客流进行操作的时候才能达到目的,即,对 Stream<String> 进行操作,此时flatMap就可以达到目的
List<String> distinctStrs1 = Arrays.stream(strs)
// 相当于吧 str对象映射成字符串流对象
.map(str -> str.split("")) // 映射成为Stream<String[]>
// 扁平化为 字符流对象
.flatMap(Arrays::stream) // 扁平化为Stream<String>
.distinct()
.collect(Collectors.toList());
System.out.println(distinctStrs1.toString());
/*
// flatMap将由map映射得到的Stream<String[]>,转换成由各个字符串数组映射成的流Stream<String>,再将这些小的流扁平化成为一个由所有字符串构成的大流Steam<String>,从而能够达到我们的目的。
//
// 与map类似,flatMap也提供了针对特定类型的映射操作:
// flatMapToDouble(Function<? super T,? extends DoubleStream> mapper),flatMapToInt(Function<? super T,? extends IntStream> mapper),flatMapToLong(Function<? super T,? extends LongStream> mapper)
//
// */
- 未完待续