大家好,我是君哥。
Java8 中增加了 Stream 处理,可以配合 Lambda 表达式来使用,让操作集合非常便利。虽然我们平时经常使用 Stream,但用到的方法其实非常少,这篇文章就来完整的介绍 Stream 的使用。
Stream 提供的方法非常多,按照调用当前方法是否结束流处理,可以分为中间操作和结束操作。
对于中间操作,又可以分为有状态的操作和无状态操作:
- 无状态的操作是指当前元素的操作不受前面元素的影响。
- 有状态的操作是指当前元素的操作需要等所有元素处理完之后才能进行。
对于结束操作,又可以分为短路操作和非短路操作,具体如下:
- 短路操作是指不需要处理完全部的元素就可以结束。
- 非短路操作是指必须处理完所有元素才能结束。
1 创建 Stream
1.1 使用集合创建
List<Integer> list = Arrays.asList(5, 2, 3, 1, 4); Stream stream = list.stream();
1.2 使用数组创建
String[] array={"ab", "abc", "abcd", "abcde", "abcdef" }; Stream<String> stream = Arrays.stream(array);
1.3 使用 Stream 静态方法
Stream<String> stream = Stream.of("ab", "abc", "abcd", "abcde", "abcdef"); Stream<Integer> stream2 = Stream.iterate(0, (x) -> x + 3).limit(5); stream2.forEach(r -> System.out.print(r + " ")); System.out.println(); Stream<Integer> stream3 = Stream.generate(new Random()::nextInt).limit(3); stream3.forEach(r -> System.out.print(r + " "));
上面代码输出如下:
0 3 6 9 12
-150231306 -1769565695 102740625
2 无状态操作
2.1 map
接收一个函数作为入参,把这个函数应用到每个元素上,执行结果组成一个新的 stream 返回。
案例 1:对整数数组每个元素加 3 :
List<Integer> list = Arrays.asList(5, 2, 3, 1, 4); List<Integer> newList = list.stream().map(x -> x + 3).collect(Collectors.toList()); System.out.println("newList:" + newList);
上面代码输出结果如下:
newList:[8, 5, 6, 4, 7]
案例 2:把字符串数组的每个元素转换为大写:
List<String> list = Arrays.asList("ab", "abc", "abcd", "abcde", "abcdef"); List<String> newList = list.stream().map(String::toUpperCase).collect(Collectors.toList()); System.out.println("newList:" + newList);
上面代码输出结果如下:
newList:[AB, ABC, ABCD, ABCDE, ABCDEF]
2.2 mapToXXX
包括三个方法:mapToInt、mapToDouble、mapToLong
案例 3:把字符串数组转为整数数组:
List<String> list = Arrays.asList("ab", "abc", "abcd", "abcde", "abcdef"); int[] newList = list.stream().mapToInt(r -> r.length()).toArray(); System.out.println("newList:" + Arrays.toString(newList));
上面代码输出结果如下:
newList:[2, 3, 4, 5, 6]
2.3 flatMap
flatMap接收函数作为入参,然后把集合中每个元素转换成一个 stream,再把这些 stream 组成一个新的 stream,是拆分单词很好的工具。如下图:
案例 4:把一个字符串数组转成另一个字符串数组:
List<String> list = Arrays.asList("ab-abc-abcd-abcde-abcdef", "5-2-3-1-4"); List<String> newList = list.stream().flatMap(s -> Arrays.stream(s.split("-"))).collect(Collectors.toList()); System.out.println("newList:" + newList);
上面代码输出结果:
newList:[ab, abc, abcd, abcde, abcdef, 5, 2, 3, 1, 4]
2.4 flatMapToXXX
类似于 flatMap,返回一个 XXXStream。
包括三个方法:flatMapToInt、flatMapToLong、flatMapToDouble
案例 5:对给定的二维整数数组求和:
int[][] data = {{1,2},{3,4},{5,6}}; IntStream intStream = Arrays.stream(data).flatMapToInt(row -> Arrays.stream(row)); System.out.println(intStream.sum());
输出结果为:21。
2.5 filter
筛选功能,按照一定的规则将符合条件的元素提取到新的流中。
定义一个学生类,包含姓名、年龄、性别、考试成绩四个属性:
class Student{ private String name; private Integer age; private String sex; private Integer score; public Student(String name, Integer age, String sex, Integer score){ this.name = name; this.age = age; this.score = score; this.sex = sex; } //省略getters/setters }
案例 6:找出考试成绩在 90 分以上的学生姓名:
List<Student> students = new ArrayList<>(); students.add(new Student("Mike", 10, "male", 88)); students.add(new Student("Jack", 13,"male", 90)); students.add(new Student("Lucy", 15,"female", 100)); students.add(new Student("Jessie", 12,"female", 78)); students.add(new Student("Allon", 16,"female", 92)); students.add(new Student("Alis", 22,"female", 50)); List<String> nameList = students.stream().filter(x -> x.getScore() >= 90).map(Student::getName).collect(Collectors.toList()); System.out.print("考试成绩90分以上的学生姓名:" + nameList);
输出如下:
考试成绩90分以上的学生姓名:[Jack, Lucy, Allon]
2.6 peek
返回由 stream 中元素组成的新 stream,用给定的函数作用在新 stream 的每个元素上。传入的函数是一个 Consume 类型的,没有返回值,因此并不会改变原 stream 中元素的值。peek 主要用是 debug,可以方便地 查看流处理结果是否正确。
案例 7:过滤出 stream 中长度大于 3 的字符串并转为大写:
Stream.of("one", "two", "three", "four") .filter(e -> e.length() > 3) .peek(e -> System.out.println("Filtered value: " + e)) .map(String::toUpperCase) .peek(e -> System.out.println("Mapped value: " + e)) .collect(Collectors.toList());
输出结果如下:
Filtered value: three
Mapped value: THREE
Filtered value: four
Mapped value: FOUR
2.7 unordered
把一个有序的 stream 转成一个无序 stream ,如果原 stream 本身就是无序的,可能会返回原始的 stream。
案例 8:把一个有序数组转成无序
Arrays.asList("1", "2", "3", "4", "5") .parallelStream() .unordered() .forEach(r -> System.out.print(r + " "));
每次执行输出的结果不一样,下面是一次输出的结果:
3 5 4 2 1
3 有状态操作
3.1 distinct
去重功能。
案例 9 :去掉字符串数组中的重复字符串
String[] array = { "a", "b", "b", "c", "c", "d", "d", "e", "e"}; List<String> newList = Arrays.stream(array).distinct().collect(Collectors.toList()); System.out.println("newList:" + newList);
输出结果:
newList:[a, b, c, d, e]
3.2 limit
限制从 stream 中获取前 n 个元素。
案例 10 :从数组中获取前 5 个元素
String[] array = { "c", "c", "a", "b", "b", "e", "e", "d", "d"}; List<String> newList = Arrays.stream(array).limit(5).collect(Collectors.toList()); System.out.println("newList:" + newList);
输出结果:
newList:[c, c, a, b, b]
3.3 skip
跳过 Stream 中前 n 个元素
案例 11:从数组中获取第 5 个元素之后的元素
String[] array = { "a", "b", "c", "d", "e", "f", "g", "h", "i"}; List<String> newList = Arrays.stream(array).skip(5).collect(Collectors.toList()); System.out.println("newList:" + newList);
输出结果:
newList:[f, g, h, i]
3.4 sorted
排序功能。
案例 12:对给定数组进行排序
String[] array = { "c", "c", "a", "b", "b", "e", "e", "d", "d"}; List<String> newList = Arrays.stream(array).sorted().collect(Collectors.toList()); System.out.println("newList:" + newList);
输出结果:
newList:[a, b, b, c, c, d, d, e, e]
案例 13:按照学生成绩进行排序
List<Student> students = new ArrayList<>(); students.add(new Student("Mike", 10, "male", 88)); students.add(new Student("Jack", 13,"male", 90)); students.add(new Student("Lucy", 15,"female", 100)); students.add(new Student("Jessie", 12,"female", 78)); students.add(new Student("Allon", 16,"female", 92)); students.add(new Student("Alis", 22,"female", 50)); List<String> nameList = students.stream().sorted(Comparator.comparing(Student::getScore)).map(Student::getName).collect(Collectors.toList()); System.out.print("按成绩排序输出学生姓名:" + nameList);
输出结果:
考试成绩90分以上的学生姓名:[Alis, Jessie, Mike, Jack, Allon, Lucy]