Java Stream
Java API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。
Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。
Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。
什么是 Stream?
Stream(流)是一个来自数据源的元素队列并支持聚合操作,元素是特定类型的对象,形成一个队列。Java中的Stream并不会存储元素,而是按需计算。
Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。
数据源 流的来源。可以是集合,数组,I/O channel, 产生器generator 等。
聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。
和以前的Collection操作不同, Stream操作还有两个基础的特征:
Pipelining: 中间操作都会返回流对象本身。这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
内部迭代:以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。
Stream将要处理的元素集合看作一种流,在流的过程中,借助Stream API对流中的元素进行操作,比如:筛选、排序、聚合等。
Stream可以由数组或集合创建,对流的操作分为两种:
- 中间操作,每次返回一个新的流,可以有多个。
- 终端操作,每个流只能进行一次终端操作,终端操作结束后流无法再次使用。终端操作会产生一个新的集合或值。
使用 Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。
Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
Stream的特点:
-
不是数据结构,stream不存储数据,而是按照特定的规则对数据进行计算,一般会输出结果。
-
不会修改原来的数据源,它会将操作后的数据保存到另外一个对象中,通常情况下会产生一个新的集合或一个值。
-
惰性求值,流在中间处理过程中,只是对操作进行了记录,并不会立即执行,需要等到执行终止操作的时候才会进行实际的计算。
-
无状态:指元素的处理不受之前元素的影响
-
有状态:指该操作只有拿到所有元素之后才能继续下去
-
非短路操作:指必须处理所有元素才能得到最终结果
-
短路操作:指遇到某些符合条件的元素就可以得到最终结果,如 A || B,只要 A 为 true,则无需判断 B 的结果
什么是流
流是从支持数据处理操作的源生成的元素序列,源可以是数组、文件、集合、函数。流不是集合元素,它不是数据结构并不保存数据,它的主要目的在于计算
生成流的方式
1.通过集合生成,应用中最常用的一种, 通过集合的stream方法生成流(通过 java.util.Collection.stream() 方法用集合创建流)
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);
Stream<Integer> stream = integerList.stream();
List<String> list = Arrays.asList("a", "b", "c");
// 创建一个顺序流
Stream<String> stream = list.stream();
// 创建一个并行流
Stream<String> parallelStream = list.parallelStream();
2.通过数组生成:使用java.util.Arrays.stream(T[] array)方法用数组创建流
int[] intArr = new int[]{1, 2, 3, 4, 5};
IntStream stream = Arrays.stream(intArr);
通过Arrays.stream方法生成流,并且该方法生成的流是数值流【即IntStream】而不是Stream。补充一点使用数值流可以避免计算过程中拆箱装箱,提高性能。
Stream API提供了mapToInt、mapToDouble、mapToLong三种方式将对象流【即Stream】转换成对应的数值流,同时提供了boxed方法将数值流转换为对象流
3.通过值生成, 通过Stream的of方法生成流,通过Stream的empty方法可以生成一个空流
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6);
4.通过文件生成,通过Files.line方法得到一个流,并且得到的每个流是给定文件中的一行
Stream<String> lines = Files.lines(Paths.get("data.txt"), Charset.defaultCharset());
5.通过函数生成 提供了iterate和generate两个静态方法从函数中生成流
- iterate: iterate方法接受两个参数,第一个为初始化值,第二个为进行的函数操作,因为iterator生成的流为无限流,通过limit方法对流进行了截断,只生成5个偶数
- generator:generate方法接受一个参数,方法参数类型为Supplier,由它为流提供值。generate生成的流也是无限流,因此通过limit对流进行了截断
//iterate
Stream<Integer> stream1 = Stream.iterate(0, n -> n + 2).limit(5);
//generator
Stream<Double> stream2 = Stream.generate(Math::random).limit(5);
stream3.forEach(System.out::println);
示例代码
/**
* @ClassName CreateStream
* @Desc 流生成的五种方式
* 1.通过集合生成,应用中最常用的一种
*
* @Author diandian
* @Date 2022/11/11 21:15
**/
public class CreateStream {
/**
* 1.通过集合生成,应用中最常用的一种, 通过集合的stream方法生成流
*/
private void CreateStream1(){
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);
Stream<Integer> stream = integerList.stream();
System.out.println(stream.collect(Collectors.toList()));
}
/**
* 2.通过数组生成
*
* 通过Arrays.stream方法生成流,并且该方法生成的流是数值流【即IntStream】而不是Stream<Integer>。补充一点使用数值流可以避免计算过程中拆箱装箱,提高性能。
*
* Stream API提供了mapToInt、mapToDouble、mapToLong三种方式将对象流【即Stream】转换成对应的数值流,同时提供了boxed方法将数值流转换为对象流
*/
private void CreateStream2(){
int[] intArr = new int[]{1, 2, 3, 4, 5};
IntStream stream = Arrays.stream(intArr);
System.out.println("最大值:"+stream.max().getAsInt());
}
/**
* 3.通过值生成, 通过Stream的of方法生成流,通过Stream的empty方法可以生成一个空流
*/
private void CreateStream3(){
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6);
System.out.println(integerStream.collect(Collectors.toList()));
}
/**
* 4.通过文件生成,通过Files.line方法得到一个流,并且得到的每个流是给定文件中的一行
*/
private void CreateStream4() throws IOException {
Stream<String> lines = Files.lines(Paths.get("spring-boot-java8/doc/data.txt"), Charset.defaultCharset());
System.out.println(lines.collect(Collectors.toList()));
}
/**
* 5.通过函数生成 提供了iterate和generate两个静态方法从函数中生成流
* iterate: iterate方法接受两个参数,第一个为初始化值,第二个为进行的函数操作,因为iterator生成的流为无限流,通过limit方法对流进行了截断,只生成5个偶数
* generator:generate方法接受一个参数,方法参数类型为Supplier,由它为流提供值。generate生成的流也是无限流,因此通过limit对流进行了截断
*
*/
private void CreateStream5(){
//iterate
Stream<Integer> stream1 = Stream.iterate(0, n -> n + 2).limit(5);
System.out.println(stream1.collect(Collectors.toList()));
//generator
Stream<Double> stream2 = Stream.generate(Math::random).limit(5);
System.out.println(stream2.collect(Collectors.toList()));
}
public static void main(String[] args) throws IOException {
CreateStream stream = new CreateStream();
stream.CreateStream1();
stream.CreateStream2();
stream.CreateStream3();
stream.CreateStream4();
stream.CreateStream5();
}
}
流的常用创建方法
- 使用 Collection 下的 stream() 和 parallelStream() 方法
Stream<String> stringStream = list.stream(); //获取一个顺序流
stringStream.forEach(s -> System.out.println(s));
Stream<String> parallelStream = list.parallelStream(); //获取一个并行流
parallelStream.forEach(s -> System.out.println(s));
// 并行流 使用 分数不为null的学生人数
Long count = userPoList.parallelStream().filter(p -> null != p.getScore()).count();
//使用 parallelStream 来输出空字符串的数量
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");// 获取空字符串的数量
long count = strings.parallelStream().filter(string -> string.isEmpty()).count();
- 使用 Arrays 中的 stream() 方法,将数组转成流
//使用 Arrays 中的 stream() 方法,将数组转成流
Integer[] nums = new Integer[]{20, 3, 5, 8, 9, 4, 1, 0, 23, 11, 20, 4, 5, 3, 5};
Stream<Integer> stream = Arrays.stream(nums);
//去重
Stream<Integer> disInt = stream.distinct();
//排序输出
disInt.sorted(Comparator.naturalOrder()).forEach(n -> System.out.print(n+" "));
- 使用Stream中的静态方法:of()、iterate()、generate()
//使用Stream中的静态方法:of()、iterate()、generate()
Stream<Integer> stream = Stream.of(10, 3, 6, 8, 0, 2, 4, 7);
//获取最大值
Integer max = stream.max(Comparator.naturalOrder()).get();
System.out.println("max = " + max);
//迭代,步长为3,7次
Stream<Integer> stream1 = Stream.iterate(0, (x) -> x + 3).limit(7);
stream1.forEach(n-> System.out.print(n + " , "));
//随机生成8个数
Stream<Double> stream2 = Stream.generate(Math::random).limit(8);
stream2.forEach(System.out::println);
- 使用 BufferedReader.lines() 方法,将每行内容转成流
//使用 BufferedReader.lines() 方法,将每行内容转成流
BufferedReader reader = new BufferedReader(new FileReader("spring-boot-java8/doc/meiwen.txt"));
Stream<String> lineStream = reader.lines();
lineStream.forEach(System.out::println);
- 使用 Pattern.splitAsStream() 方法,将字符串分隔成流:
//使用 Pattern.splitAsStream() 方法,将字符串分隔成流:
Pattern pattern = Pattern.compile("\\|");
String str = "10687699|10687722|10687725|10687729|10687751|10687759|10687772|10687773|10687774";
Stream<String> stringStream = pattern.splitAsStream(str);
stringStream.forEach(System.out::println);
本次分享就到此,后续还会不断更新!如果对你有帮助,请点赞支持一下!
共同学习,写下你的评论
评论加载中...
作者其他优质文章