Java8的 Stream 流学习与使用总结

tech2022-10-27  122

1. Stream 流介绍

Stream的原理:将要处理的元素看做一种流,流在管道中传输,并且可以在管道的节点上处理,包括过滤筛选、去重、排序、聚合等。元素流在管道中经过中间操作的处理,最后由最终操作得到前面处理的结果。 Stream 流的使用总是按照一定的步骤进行,可以抽象出下面的使用流程。 数据源 -> 数据处理/转换-> 结果处理

1.1. 数据源 数据源也就是数据的来源,可以通过多种方式获得 Stream 数据源,下面列举几种常见的获取方式。 (1)Collection.stream():从集合获取流。 (2)Collection.parallelStream():从集合获取并行流。 (3)Arrays.stream(T array) or Stream.of():从数组获取流。 (4)BufferedReader.lines():从输入流中获取流。 (5)IntStream.of():从静态方法中获取流。 (6)Stream.generate():自己生成流。

1.2. 数据处理 数据处理/转换步骤可以有多个操作,这步也被称为中间操作。数据处理演示。 @Test public void streamDemo(){ List nameList = Arrays.asList(“Darcy”, “Chris”, “Linda”, “Sid”, “Kim”, “Jack”, “Poul”, “Peter”); // 1. 筛选出名字长度为4的 // 2. 名字前面拼接 This is // 3. 遍历输出 nameList.stream() .filter(name -> name.length() == 4) .map(name -> "This is "+name) .forEach(name -> System.out.println(name)); } // 输出结果 // This is Jack // This is Poul 数据处理/转换操作有 map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered 等。

1.3. 收集结果 结果处理是流处理的最后一步,执行完这一步之后流会被彻底用尽,流也不能继续操作了。 常见的结果处理操作有 forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator 等。 下面演示了简单的结果处理的例子。将集合元素转换成为大写然后收集结果,遍历输出

@Test public void toUpperCaseDemo() { List nameList = Arrays.asList(“Darcy”, “Chris”, “Linda”, “Sid”, “Kim”, “Jack”, “Poul”, “Peter”); List upperCaseNameList = nameList.stream() .map(String::toUpperCase) .collect(Collectors.toList()); upperCaseNameList.forEach(name -> System.out.println(name + “,”)); } // 输出结果 // DARCY,CHRIS,LINDA,SID,KIM,JACK,POUL,PETER,

2. Stream 流使用

Stream 流在使用时候总是借助于 Lambda 表达式进行操作,Stream 流的操作也有很多种方式,下面列举的是常用的一些操作。

2.1. Stream 流获取 获取 Stream 的几种方式在上面的 Stream 数据源里已经介绍过了,下面是针对上面介绍的几种获取 Stream 流的使用示例。

@Test public void createStream() throws FileNotFoundException { List nameList = Arrays.asList(“Darcy”, “Chris”, “Linda”, “Sid”, “Kim”, “Jack”, “Poul”, “Peter”); String[] nameArr = {“Darcy”, “Chris”, “Linda”, “Sid”, “Kim”, “Jack”, “Poul”, “Peter”}; // 集合获取 Stream 流 Stream nameListStream = nameList.stream(); // 集合获取并行 Stream 流 Stream nameListStream2 = nameList.parallelStream(); // 数组获取 Stream 流 Stream nameArrStream = Stream.of(nameArr); // 数组获取 Stream 流 Stream nameArrStream1 = Arrays.stream(nameArr); // 文件流获取 Stream 流 BufferedReader bufferedReader = new BufferedReader(new FileReader(“README.md”)); Stream linesStream = bufferedReader.lines(); // 从静态方法获取流操作 IntStream rangeStream = IntStream.range(1, 10); rangeStream.limit(10).forEach(num -> System.out.print(num+",")); System.out.println(); IntStream intStream = IntStream.of(1, 2, 3, 3, 4); intStream.forEach(num -> System.out.print(num+",")); }

2.2. forEach forEach 是 Stream 流中的一个重要方法,用于遍历 Stream 流,它支持传入一个标准的 Lambda 表达式。但是它的遍历不能通过 return/break 进行终止。同时它也是一个 terminal 操作,执行之后 Stream 流中的数据会被消费掉。

List numberList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9); numberList.stream().forEach(number -> System.out.println(number+",")); // 输出结果 // 1,2,3,4,5,6,7,8,9,

2.3. map / flatMap(转换) 使用 map 把对象一对一映射成另一种对象或者形式。把数字值乘以2

@Test public void mapTest() { List numberList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9); // 映射成 2倍数字 List collect = numberList.stream() .map(number -> number * 2) .collect(Collectors.toList()); collect.forEach(number -> System.out.print(number + “,”)); System.out.println();

numberList.stream() .map(number -> "数字 " + number + ",") .forEach(number -> System.out.println(number));

} // 输出结果 // 2,4,6,8,10,12,14,16,18, // 数字 1,数字 2,数字 3,数字 4,数字 5,数字 6,数字 7,数字 8,数字 9,

再比如:集合转换 public static void main(String [] args) {

Student s1 = new Student(1L, "肖战", 15, "浙江"); Student s2 = new Student(2L, "王一博", 15, "湖北"); Student s3 = new Student(3L, "杨紫", 17, "北京"); Student s4 = new Student(4L, "李现", 17, "浙江"); List<Student> students = new ArrayList<>(); students.add(s1); students.add(s2); students.add(s3); students.add(s4); testMap(students); } private static void testMap(List<Student> students) { //在地址前面加上部分信息,只获取地址输出 List<String> addresses = students.stream().map(s ->"住址:"+s.getAddress()).collect(Collectors.toList()); addresses.forEach(a ->System.out.println(a)); }

运行结果 map就是将对应的元素按照给定的方法进行转换。

上面的 map 可以把数据进行一对一的映射,而有些时候关系可能不止 1对 1那么简单,可能会有1对多。这时可以使用 flatMap。下面演示使用 flatMap把对象扁平化展开。

@Test public void flatMapTest() { Stream<List> inputStream = Stream.of( Arrays.asList(1), Arrays.asList(2, 3), Arrays.asList(4, 5, 6) ); List collect = inputStream .flatMap((childList) -> childList.stream()) .collect(Collectors.toList()); collect.forEach(number -> System.out.print(number + “,”)); } // 输出结果 // 1,2,3,4,5,6,

2.4. filter(筛选) 使用 filter 进行数据筛选,挑选出想要的元素,下面的例子演示怎么挑选出偶数数字。

@Test public void filterTest() { List numberList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9); List collect = numberList.stream() .filter(number -> number % 2 == 0) .collect(Collectors.toList()); collect.forEach(number -> System.out.print(number + “,”)); } 得到如下结果:2,4,6,8,

再比如:集合的筛选 public static void main(String [] args) {

Student s1 = new Student(1L, "肖战", 15, "浙江"); Student s2 = new Student(2L, "王一博", 15, "湖北"); Student s3 = new Student(3L, "杨紫", 17, "北京"); Student s4 = new Student(4L, "李现", 17, "浙江"); List<Student> students = new ArrayList<>(); students.add(s1); students.add(s2); students.add(s3); students.add(s4); List<Student> streamStudents = testFilter(students); streamStudents.forEach(System.out::println); } private static List<Student> testFilter(List<Student> students) { return students.stream().filter(s ->"浙江".equals(s.getAddress())).collect(Collectors.toList()); }

运行结果: 这里我们创建了四个学生,经过filter的筛选,筛选出地址是浙江的学生集合。

2.5. findFirst findFirst 可以查找出 Stream 流中的第一个元素,它返回的是一个 Optional 类型 。

@Test public void findFirstTest(){ List numberList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9); Optional firstNumber = numberList.stream() .findFirst(); System.out.println(firstNumber.orElse(-1)); } // 输出结果 // 1 findFirst 方法在查找到需要的数据之后就会返回不再遍历数据了,也因此 findFirst 方法可以对有无限数据的 Stream 流进行操作。

2.6. collect / toArray Stream 流可以轻松的转换为其他结构,下面是几种常见的示例。Stream 转换为其他数据结构

@Test public void collectTest() { List numberList = Arrays.asList(1, 1, 2, 2, 3, 3, 4, 4, 5); // to array Integer[] toArray = numberList.stream() .toArray(Integer[]::new); // to List List integerList = numberList.stream() .collect(Collectors.toList()); // to set Set integerSet = numberList.stream() .collect(Collectors.toSet()); System.out.println(integerSet); // to string String toString = numberList.stream() .map(number -> String.valueOf(number)) .collect(Collectors.joining()).toString(); System.out.println(toString); // to string split by , String toStringbJoin = numberList.stream() .map(number -> String.valueOf(number)) .collect(Collectors.joining(",")).toString(); System.out.println(toStringbJoin); } // 输出结果 // [1, 2, 3, 4, 5] // 112233445 // 1,1,2,2,3,3,4,4,5

2.7. limit / skip 获取或者扔掉前 n 个元素

@Test public void limitOrSkipTest() { // 生成自己的随机数流 List ageList = Arrays.asList(11, 22, 13, 14, 25, 26); ageList.stream() .limit(3) .forEach(age -> System.out.print(age+",")); System.out.println();

ageList.stream() .skip(3) .forEach(age -> System.out.print(age+","));

} // 输出结果 // 11,22,13, // 14,25,26,

2.8. Statistics 数学统计功能,求一组数组的最大值、最小值、个数、数据和、平均数等。

@Test public void mathTest() { List list = Arrays.asList(1, 2, 3, 4, 5, 6); IntSummaryStatistics stats = list.stream().mapToInt(x -> x).summaryStatistics(); System.out.println(“最小值:” + stats.getMin()); System.out.println(“最大值:” + stats.getMax()); System.out.println(“个数:” + stats.getCount()); System.out.println(“和:” + stats.getSum()); System.out.println(“平均数:” + stats.getAverage()); } // 输出结果 // 最小值:1 // 最大值:6 // 个数:6 // 和:21 // 平均数:3.5

2.9. groupingBy 分组聚合功能,和数据库的 Group by 的功能一致。

@Test public void groupByTest() { List ageList = Arrays.asList(11, 22, 13, 14, 25, 26); Map<String, List> ageGrouyByMap = ageList.stream() .collect(Collectors.groupingBy(age -> String.valueOf(age / 10))); ageGrouyByMap.forEach((k, v) -> { System.out.println(“年龄” + k + “0多岁的有:” + v); }); } // 输出结果 // 年龄10多岁的有:[11, 13, 14] // 年龄20多岁的有:[22, 25, 26]

2.10. partitioningBy

public void partitioningByTest() { List ageList = Arrays.asList(11, 22, 13, 14, 25, 26); Map<Boolean, List> ageMap = ageList.stream() .collect(Collectors.partitioningBy(age -> age > 18)); System.out.println(“未成年人:” + ageMap.get(false)); System.out.println(“成年人:” + ageMap.get(true)); } // 输出结果 // 未成年人:[11, 13, 14] // 成年人:[22, 25, 26]

2.11 distinct(去重) public static void main(String [] args) {

testDistinct1(); } private static void testDistinct1() { //简单字符串的去重 List<String> list = Arrays.asList("111","222","333","111","222"); list.stream().distinct().forEach(System.out::println); }

运行结果:

public static void main(String [] args) {

testDistinct2(); } private static void testDistinct2() { //引用对象的去重,引用对象要实现hashCode和equal方法,否则去重无效 Student s1 = new Student(1L, "肖战", 15, "浙江"); Student s2 = new Student(2L, "王一博", 15, "湖北"); Student s3 = new Student(3L, "杨紫", 17, "北京"); Student s4 = new Student(4L, "李现", 17, "浙江"); Student s5 = new Student(1L, "肖战", 15, "浙江"); List<Student> students = new ArrayList<>(); students.add(s1); students.add(s2); students.add(s3); students.add(s4); students.add(s5); students.stream().distinct().forEach(System.out::println); }

运行结果: 可以看出,两个重复的“肖战”同学进行了去重,这不仅因为使用了distinct()方法,而且因为Student对象重写了equals和hashCode()方法,否则去重是无效的。

2.12 sorted(排序) public static void main(String [] args) {

testSort1(); } private static void testSort1() { List<String> list = Arrays.asList("333","222","111"); list.stream().sorted().forEach(System.out::println); }

运行结果: public static void main(String [] args) {

testSort2(); } private static void testSort2() { Student s1 = new Student(1L, "肖战", 15, "浙江"); Student s2 = new Student(2L, "王一博", 15, "湖北"); Student s3 = new Student(3L, "杨紫", 17, "北京"); Student s4 = new Student(4L, "李现", 17, "浙江"); List<Student> students = new ArrayList<>(); students.add(s1); students.add(s2); students.add(s3); students.add(s4); students.stream() .sorted((stu1,stu2) ->Long.compare(stu2.getId(), stu1.getId())) .sorted((stu1,stu2) -> Integer.compare(stu2.getAge(),stu1.getAge())) .forEach(System.out::println); }

运行结果: 上面指定排序规则,先按照学生的id进行降序排序,再按照年龄进行降序排序

2.13 reduce(聚合) public static void main(String [] args) { testReduce(); }

private static void testReduce() { List list = Arrays.asList(“欢”,“迎”,“你”); String appendStr = list.stream().reduce(“北京”,(a,b) -> a+b); System.out.println(appendStr); } 运行结果: 2.14 min(求最小值) public static void main(String [] args) { testMin(); }

private static void testMin() { Student s1 = new Student(1L, “肖战”, 14, “浙江”); Student s2 = new Student(2L, “王一博”, 15, “湖北”); Student s3 = new Student(3L, “杨紫”, 17, “北京”); Student s4 = new Student(4L, “李现”, 17, “浙江”); List students = new ArrayList<>(); students.add(s1); students.add(s2); students.add(s3); students.add(s4); Student minS = students.stream().min((stu1,stu2) ->Integer.compare(stu1.getAge(),stu2.getAge())).get(); System.out.println(minS.toString()); }

运行结果: 上面是求所有学生中年龄最小的一个,max同理,求最大值。

2.15 anyMatch/allMatch/noneMatch(匹配)

public static void main(String [] args) { testMatch(); }

private static void testMatch() { Student s1 = new Student(1L, “肖战”, 15, “浙江”); Student s2 = new Student(2L, “王一博”, 15, “湖北”); Student s3 = new Student(3L, “杨紫”, 17, “北京”); Student s4 = new Student(4L, “李现”, 17, “浙江”); List students = new ArrayList<>(); students.add(s1); students.add(s2); students.add(s3); students.add(s4); Boolean anyMatch = students.stream().anyMatch(s ->“湖北”.equals(s.getAddress())); if (anyMatch) { System.out.println(“有湖北人”); } Boolean allMatch = students.stream().allMatch(s -> s.getAge()>=15); if (allMatch) { System.out.println(“所有学生都满15周岁”); } Boolean noneMatch = students.stream().noneMatch(s -> “杨洋”.equals(s.getName())); if (noneMatch) { System.out.println(“没有叫杨洋的同学”); } }

运行结果:

anyMatch:Stream 中任意一个元素符合传入的 predicate,返回 true allMatch:Stream 中全部元素符合传入的 predicate,返回 true noneMatch:Stream 中没有一个元素符合传入的 predicate,返回 true

最新回复(0)