目录
- 1 基本过滤:返回学生列表中90分以上的
- 2 基本转换:根据学生列表返回名称列表
- 3 基本过滤和基本转换组合:返回90分以上的学生名称列表
- 4 中间操作distinct:返回字符串列表中长度小于3的字符串、转换为小写、只保留唯一的
- 5 中间操作sorted
- 集合排序(Collections.sort/new Comparator)
- Collections.sort
- new Comparator
- 6 中间操作skip/limit:将学生列表按照分数排序,返回第3名到第5名
- 7 中间操作mapToLong/mapToInt/mapToDouble:求学生列表的分数总和
- 8 终端操作max/min:返回分数最高的学生
- 9 终端操作count:统计大于90分的学生个数
- 10 终端操作allMatch/anyMatch/noneMatch:判断是不是所有学生都及格了(不小于60分)
- 11 终端操作forEach:逐行打印大于90分的学生
- 12 终端操作toArray:获取90分以上的学生数组
- 13 容器收集器toSet
- 14 容器收集器toMap
- 15 分组
1 基本过滤:返回学生列表中90分以上的
List<Student> above90List = students.stream()
.filter(t->t.getScore()>90)
.collect(Collectors.toList());
- 通过stream()得到一个Stream对象;
- 调用Stream的方法filter()过滤得到90分以上的,它的返回值依然是一个Stream;
- 为了转换为List,调用collect方法并传递Collectors.toList(),表示将结果收集到一个List中。
2 基本转换:根据学生列表返回名称列表
List<String> nameList = students.stream()
.map(Student::getName)
.collect(Collectors.toList());
- 这里使用了Stream的map函数,它的参数是一个Function函数式接口,这里传递了方法引用。
3 基本过滤和基本转换组合:返回90分以上的学生名称列表
List<String> above90Names = students.stream()
.filter(t->t.getScore()>90)
.map(Student::getName)
.collect(Collectors.toList());
- filter()和map()都需要对流中的每个元素操作一次,一起使用会不会就需要遍历两次呢?答案是否定的,只需要一次。实际上,调用filter()和map()都不会执行任何实际的操作,它们只是在构建操作的流水线,调用collect才会触发实际的遍历执行,在一次遍历中完成过滤、转换以及收集结果的任务;
- 像filter和map这种不实际触发执行、用于构建流水线、返回Stream的操作称为中间操作(intermediate operation),而像collect这种触发实际执行、返回具体结果的操作称为终端操作(terminal operation)。
4 中间操作distinct:返回字符串列表中长度小于3的字符串、转换为小写、只保留唯一的
List<String> list = Arrays.asList(new String[]{"abc", "def", "hello", "Abc"});
List<String> retList = list.stream()
.filter(s->s.length()<=3)
.map(String::toLowerCase)
.distinct()
.collect(Collectors.toList());
- 对于顺序流,内部实现时,distinct操作会使用HashSet记录出现过的元素,如果流是有顺序的,需要保留顺序,会使用LinkedHashSet。
5 中间操作sorted
//返回 对象集合以类属性一升序排序
list.stream().sorted(Comparator.comparing(类::属性一));
//返回 对象集合以类属性一降序排序 注意两种写法
list.stream().sorted(Comparator.comparing(类::属性一).reversed());//先以属性一升序,结果进行属性一降序
list.stream().sorted(Comparator.comparing(类::属性一,Comparator.reverseOrder()));//以属性一降序
//返回 对象集合以类属性一升序 属性二升序
list.stream().sorted(Comparator.comparing(类::属性一).thenComparing(类::属性二));
//返回 对象集合以类属性一降序 属性二升序 注意两种写法
list.stream().sorted(Comparator.comparing(类::属性一).reversed().thenComparing(类::属性二));//先以属性一升序,升序结果进行属性一降序,再进行属性二升序
list.stream().sorted(Comparator.comparing(类::属性一,Comparator.reverseOrder()).thenComparing(类::属性二));//先以属性一降序,再进行属性二升序
//返回 对象集合以类属性一降序 属性二降序 注意两种写法
list.stream().sorted(Comparator.comparing(类::属性一).reversed().thenComparing(类::属性二,Comparator.reverseOrder()));//先以属性一升序,升序结果进行属性一降序,再进行属性二降序
list.stream().sorted(Comparator.comparing(类::属性一,Comparator.reverseOrder()).thenComparing(类::属性二,Comparator.reverseOrder()));//先以属性一降序,再进行属性二降序
//返回 对象集合以类属性一升序 属性二降序 注意两种写法
list.stream().sorted(Comparator.comparing(类::属性一).reversed().thenComparing(类::属性二).reversed());//先以属性一升序,升序结果进行属性一降序,再进行属性二升序,结果进行属性一降序属性二降序
list.stream().sorted(Comparator.comparing(类::属性一).thenComparing(类::属性二,Comparator.reverseOrder()));//先以属性一升序,再进行属性二降序
通过以上例子我们可以发现
- Comparator.comparing(类::属性一).reversed();
- Comparator.comparing(类::属性一,Comparator.reverseOrder());
两种排序是完全不一样的,一定要区分开来:1是得到排序结果后再排序,2是直接进行排序,很多人会混淆导致理解出错,2更好理解,建议使用2
集合排序(Collections.sort/new Comparator)
Collections.sort
Collections.sort(orgDataList,(o1,o2)->{
String order1=o1.getSortOrder();
if(order1==null){
order1="";
}
String order2=o2.getSortOrder();
if(order2==null){
order2="";
}
return order1.compareTo(order2);
});
new Comparator
eduDataInfo = eduDataInfo.stream()
.sorted(new Comparator<EduExperience>() {
@Override
public int compare(EduExperience o1, EduExperience o2) {
if (o1.getEducationCode().compareTo(o2.getEducationCode()) == 0 && o1.getEducationCode().compareTo(o2.getEducationCode()) == 0) {
return o1.getSeqNum().compareTo(o2.getSeqNum());
} else if (o1.getEducationCode().compareTo(o2.getEducationCode()) == 0) {
return o2.getDegreeCode().compareTo(o1.getDegreeCode());
} else {
return o2.getEducationCode().compareTo(o1.getEducationCode());
}
}
}).collect(Collectors.toList());
6 中间操作skip/limit:将学生列表按照分数排序,返回第3名到第5名
List<Student> list = students.stream()
.sorted(Comparator.comparing(Student::getScore).reversed())
.skip(2).limit(3)
.collect(Collectors.toList());
- skip跳过流中的n个元素,如果流中元素不足n个,返回一个空流,limit限制流的长度为maxSize;
- skip和limit都是有状态的中间操作。对前n个元素,skip的操作就是过滤,对后面的元素,skip就是传递给流水线中的下一个操作。limit的一个特点是:它不需要处理流中的所有元素,只要处理的元素个数达到maxSize,后面的元素就不需要处理了,这种可以提前结束的操作称为短路操作。
7 中间操作mapToLong/mapToInt/mapToDouble:求学生列表的分数总和
double sum = students.stream().mapToDouble(Student::getScore).sum();
为避免装箱/拆箱,提高性能,Stream提供了返回基本类型特定流的方法。
8 终端操作max/min:返回分数最高的学生
Student student = students.stream().max(Comparator.comparing(Student::getGrade)).get();
或
Student student = students.stream().min(Comparator.comparing(Student::getGrade).reversed()).get();
这里假定students不为空
9 终端操作count:统计大于90分的学生个数
long above90Count = students.stream().filter(t->t.getScore()>90).count();
10 终端操作allMatch/anyMatch/noneMatch:判断是不是所有学生都及格了(不小于60分)
boolean allPass = students.stream().allMatch(t->t.getScore()>=60);
- allMatch:只有在流中所有元素都满足条件的情况下才返回true;
- anyMatch:只要流中有一个元素满足条件就返回true;
- noneMatch:只有流中所有元素都不满足条件才返回true;
如果流为空,那么这几个函数的返回值都是true。
11 终端操作forEach:逐行打印大于90分的学生
students.stream().filter(t->t.getScore()>90).forEach(System.out::println);
12 终端操作toArray:获取90分以上的学生数组
Student[] above90Arr = students.stream().filter(t->t.getScore()>90).toArray(Student[]::new);
13 容器收集器toSet
toSet的使用与toList类似,只是它可以排重。toList背后的容器是ArrayList, toSet背后的容器是HashSet。
14 容器收集器toMap
toMap将元素流转换为一个Map,我们知道,Map有键和值两部分,toMap至少需要两个函数参数,一个将元素转换为键,另一个将元素转换为值。
- 将学生流转换为学生名称和分数的Map
Map<String, Double> nameScoreMap = students.stream().collect(Collectors.toMap(Student::getName, Student::getScore, (oldValue, value)->value));
- 转换学生流为学生id和学生对象的Map:
Map<String, Student> byIdMap = students.stream().collect(Collectors.toMap(Student::getId, Function.identity(), (oldValue, value)->value));
- 得到字符串与其长度的Map
Map<String, Integer> strLenMap = Stream.of("abc", "hello", "abc").collect(Collectors.toMap(Function.identity(), t->t.length(), (oldValue, value)->value));
15 分组
分组类似于数据库查询语言SQL中的group by语句,它将元素流中的每个元素分到一个组,可以针对分组再进行处理和收集。
- 将学生流按照年级进行分组
Map<String, List<Student>> groups = students.stream().collect(Collectors.groupingBy(Student::getGrade));
- 统计每个年级的学生个数
Map<String, Long> gradeCountMap = students.stream().collect(groupingBy(Student::getGrade, counting()));
- 统计一个单词流中每个单词的个数,按出现顺序排序
Map<String, Long> wordCountMap = Stream.of("hello", "world", "abc", "hello").collect( groupingBy(Function.identity(), LinkedHashMap::new, counting()));
- 对学生按年级分组,得到学生名称列表
Map<String, List<String>> gradeNameMap = students.stream().collect(groupingBy(Student::getGrade, mapping(Student::getName, toList())));
- 将学生按年级分组,分组内的学生按照分数由高到低进行排序
Map<String, List<Student>> gradeStudentMap = students.stream().collect(groupingBy(Student::getGrade, collectingAndSort(toList(), Comparator.comparing(Student::getScore).reversed())));
- 将学生按年级分组,分组后,每个分组只保留不及格的学生(低于60分)
Map<String, List<Student>> gradeStudentMap = students.stream()
.collect(groupingBy(Student::getGrade, collectingAndFilter(toList(), t->t.getScore()<60)));