Stream流
Stream流的介绍
stream
不存储数据,而是按照特定的规则对数据进行计算,一般会输出结果;stream
不会改变数据源,通常情况下会产生一个新的集合;stream
具有延迟执行特性,只有调用终端操作时,中间操作才会执行。- 对
stream
操作分为终端操作和中间操作,那么这两者分别代表什么呢?终端操作
:会消费流,这种操作会产生一个结果的,如果一个流被消费过了,那它就不能被重用的。中间操作
:中间操作会产生另一个流。因此中间操作可以用来创建执行一系列动作的管道。一个特别需要注意的点是:中间操作不是立即发生的。相反,当在中间操作创建的新流上执行完终端操作后,中间操作指定的操作才会发生。所以中间操作是延迟发生的,中间操作的延迟行为主要是让流API能够更加高效地执行。 stream
不可复用,对一个已经进行过终端操作的流再次调用,会抛出异常。
串行 Stream 流
1.获取串行流的两种方式:
所有的 Collection 集合都可以通过 stream 默认方法获取流:list.stream();
Stream 接口的静态方法 of 可以获取数组对应的流:Stream.of(6,1,5,4,3);
// 集合获取流
// Collection接口中的方法: default Stream<E> stream() 获取流
List<String> list = new ArrayList<>();
Stream<String> stream1 = list.stream();
Set<String> set = new HashSet<>();
Stream<String> stream2 = set.stream();
Vector<String> vector = new Vector<>();
Stream<String> stream3 = vector.stream();
// Map获取流
Map<String, String> map = new HashMap<>();
Stream<String> keyStream = map.keySet().stream();
Stream<String> valueStream = map.values().stream();
Stream<Map.Entry<String, String>> entryStream = map.entrySet().stream();
2.Stream常用方法
@Test
public void test() {
List<String> strings = Arrays.asList("abc", "def", "gkh", "abc");
//返回符合条件的stream
Stream<String> stringStream = strings.stream().filter(s -> "abc".equals(s));
//计算流符合条件的流的数量
long count = stringStream.count();
//forEach遍历->打印元素
strings.stream().forEach(System.out::println);
//limit 获取到1个元素的stream
Stream<String> limit = strings.stream().limit(1);
//toArray 比如我们想看这个limitStream里面是什么,比如转换成String[],比如循环
String[] array = limit.toArray(String[]::new);
//map 对每个元素进行操作返回新流
Stream<String> map = strings.stream().map(s -> s + "22");
//sorted 排序并打印
strings.stream().sorted().forEach(System.out::println);
//Collectors collect 把abc放入容器中
List<String> collect = strings.stream().filter(string -> "abc".equals(string)).collect(Collectors.toList());
//把list转为string,各元素用,号隔开
String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(","));
//对数组的统计,比如用
List<Integer> number = Arrays.asList(1, 2, 5, 4);
IntSummaryStatistics statistics = number.stream().mapToInt((x) -> x).summaryStatistics();
System.out.println("列表中最大的数 : "+statistics.getMax());
System.out.println("列表中最小的数 : "+statistics.getMin());
System.out.println("平均数 : "+statistics.getAverage());
System.out.println("所有数之和 : "+statistics.getSum());
//concat 合并流
List<String> strings2 = Arrays.asList("xyz", "jqx");
Stream.concat(strings2.stream(),strings.stream()).count();
//注意 一个Stream只能操作一次,不能断开,否则会报错。
Stream stream = strings.stream();
//第一次使用
stream.limit(2);
//第二次使用
stream.forEach(System.out::println);
//报错 java.lang.IllegalStateException: stream has already been operated upon or closed
//但是可以这样, 连续使用
stream.limit(2).forEach(System.out::println);
}
3.Stream注意事项(重要)
Stream不存储数据,而是按照特定的规则对数据进行计算,一般会输出结果
Stream只能操作一次
Stream方法返回的是新的流
Stream不调用终结方法,中间的操作不会执行
并行 parallelStream 流
1.获取并行流的两种方式:
直接获取并行的流:
将串行流转成并行流:
// 直接获取并行的流
ArrayList<Integer> list = new ArrayList<>();
Stream<Integer> stream = list.parallelStream();
// 将串行流转成并行流
ArrayList<Integer> list2 = new ArrayList<>();
Stream<Integer> stream = list2.stream().parallel();
2.并行流的与串行流的区别
顾名思义,当使用顺序方式去遍历时,每个item读完后再读下一个item。而使用并行去遍历时,数组会被分成多个段,其中每一个都在不同的线程中处理,然后将结果一起输出。
- 串行有序
- 并行无序
-
并行流的效率是要比串行流要高,底层使用Fork/Join框架进行处理
-
parallelStream是线程不安全的。
3.解决并行流的线程安全问题:多线程下,使用并行流会有线程安全的问题
根据需要进行不同的处理:
使用同步代码块 synchronized (比如使用forEach循环处理时)
使用线程安全的集合 Vector、Collections.synchronizedList(list)
调用Stream流的 collect/toArray 方法
4.并行流使用注意事项
1、parallelStream是线程不安全的
2、parallelStream适用的场景是CPU密集型的,只是做到别浪费CPU,假如本身电脑CPU
的负载很大,那还到处用并行流,那并不能起到作用
3、I/O密集型 磁盘I/O、网络I/O都属于I/O操作,这部分操作是较少消耗CPU资源,通常不是CPU密集型操作,一般并行流中不适用于I/O密集型的操作,相反,它们是对系统资源(如磁盘、网络、内存等)的频繁访问和等待的操作。就比如使用并行流进行大批量的消息推送,涉及到了大量I/O,使用并行流反而慢了很多
4、在使用并行流的时候是无法保证元素的顺序的,也就是即使你用了同步集合也只能保
证元素都正确但无法保证其中的顺序
并行流使用场景:在数据量比较大的情况下,CPU负载本身不是很高,CPU密集型的操作,不要求顺序执行的时候,可以使用并行流。