目录

  • 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());
  1. 通过stream()得到一个Stream对象;
  2. 调用Stream的方法filter()过滤得到90分以上的,它的返回值依然是一个Stream;
  3. 为了转换为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());
  1. filter()和map()都需要对流中的每个元素操作一次,一起使用会不会就需要遍历两次呢?答案是否定的,只需要一次。实际上,调用filter()和map()都不会执行任何实际的操作,它们只是在构建操作的流水线,调用collect才会触发实际的遍历执行,在一次遍历中完成过滤、转换以及收集结果的任务;
  2. 像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()));//先以属性一升序,再进行属性二降序

通过以上例子我们可以发现

  1. Comparator.comparing(类::属性一).reversed();
  2. 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至少需要两个函数参数,一个将元素转换为键,另一个将元素转换为值。

  1. 将学生流转换为学生名称和分数的Map
Map<String, Double> nameScoreMap = students.stream().collect(Collectors.toMap(Student::getName, Student::getScore, (oldValue, value)->value));
  1. 转换学生流为学生id和学生对象的Map:
Map<String, Student> byIdMap = students.stream().collect(Collectors.toMap(Student::getId, Function.identity(), (oldValue, value)->value));
  1. 得到字符串与其长度的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语句,它将元素流中的每个元素分到一个组,可以针对分组再进行处理和收集。

  1. 将学生流按照年级进行分组
Map<String, List<Student>> groups = students.stream().collect(Collectors.groupingBy(Student::getGrade));
  1. 统计每个年级的学生个数
Map<String, Long> gradeCountMap = students.stream().collect(groupingBy(Student::getGrade, counting()));
  1. 统计一个单词流中每个单词的个数,按出现顺序排序
Map<String, Long> wordCountMap = Stream.of("hello", "world", "abc", "hello").collect( groupingBy(Function.identity(), LinkedHashMap::new, counting()));
  1. 对学生按年级分组,得到学生名称列表
Map<String, List<String>> gradeNameMap = students.stream().collect(groupingBy(Student::getGrade, mapping(Student::getName, toList())));
  1. 将学生按年级分组,分组内的学生按照分数由高到低进行排序
Map<String, List<Student>> gradeStudentMap = students.stream().collect(groupingBy(Student::getGrade, collectingAndSort(toList(), Comparator.comparing(Student::getScore).reversed())));
  1. 将学生按年级分组,分组后,每个分组只保留不及格的学生(低于60分)
Map<String, List<Student>> gradeStudentMap = students.stream()
.collect(groupingBy(Student::getGrade, collectingAndFilter(toList(), t->t.getScore()<60)));