首先它的特点是
Stream自己不会存储元素;
Stream不会改变源对象。相反,它们会返回一个持有结果的新Stream。
Stream操作是延迟执行的。这意味者它们会等到需要结果的时候才执行。
大概分为两种 一种是 stream() 另一种是 parallelStream()
parallelStream()和stream()的区别就是支持并行执行,提高程序运行效率,也就是多线程操作。但是如果使用不当可能会发生线程安全的问题。
对比发现parallelStream执行效率要比传统的for循环和stream要快的多,
那么什么时候要用stream或者parallelStream呢?可以从以下三点入手考虑
- 是否需要并行?
- 任务之间是否是独立的?是否会引起任何竞态条件?
- 结果是否取决于任务的调用顺序?
parallelStream可能会改变之前的顺序 Stream具有平行处理能力,处理的过程会分而治之,也就是将一个大任务切分成多个小任务,这表示每个任务都是一个操作
parallelStream在中间处理时都是并行的(这里不展开说明并行的实现),所以parallelStream中间处理的结果都是乱序的,但是在使用了Collect收集器后,parallelStream被收集的结果默认都是按照集合的原序列进行排序的。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
numbers.parallelStream().forEachOrdered(System.out::println); //这个只是输出而已
List<Integer> collect = testList.parallelStream()
.map(m -> {
if (Objects.isNull(m)) {
return null;
}
return m + 1;
})
.collect(Collectors.toList());// 后缀用了这个顺序就对了
使用parallelStream的一些注意点
(1)parallelStream并行流一定要使用线程安全的对象,比如有这样的一个场景
List list = new ArrayList<>(); IntStream.rangeClosed(1, 10000).parallel().forEach(i -> list.add(i));
执行就立即报错了:
ArrayList本身就是一个线程不安全的容器,在多线程的操作下,扩容操作可能会导致产生数组越界的异常。
此时,要么使用线程安全的容器,比如Vector,要么使用collect完成串行收集。
List collect = IntStream.rangeClosed(1, 10000).parallel().boxed().collect(Collectors.toList());
(2)线程关联的ThreadLocal将会失效
这一点从第二小节就可以看出,主线程参与到parallelStream中的任务处理的过程中。如果我们处理的任务方法中包含对ThreadLocal的处理,可能除主线程之外的所有线程都获取不到自己的线程局部变量,加之ForkJoinPool中的线程是反复使用的,线程关联的ThreadLocal会发生共用的情况。
所以我的建议是,parallelStream中就不要使用ThreadLocal了,要么在任务处理方法中,第一行先进行ThreadLocal.set(),之后再由ThreadLocal.get()获取到自己的线程局部变量。
非要用ThreadLocal的话,为了规避使用不当而带来内存泄漏的风险
(3)使用parallelStream也不一定会提升性能
在CPU资源紧张的时候,使用并行流可能会带来频繁的线程上下文切换,导致并行流执行的效率还没有串行执行的效率高。
如果要使用 还是需要有一定程度的理解
一些运用的小例子
排序
新的排序 比之前的 没啥区别 还多创建一个对象 还是用之前的吧
List<User> list1 = list.stream().sorted((user1,user2)->{
return user1.getAge()-user2.getAge();
}).collect(Collectors.toList());之前的
Collections.sort(list2,(user1,user2)->{
return user1.getAge()-user2.getAge();
});//输出
list1.stream().forEach(System.out::println);
//筛选
List<User> list1 = list.stream().filter(user -> {return user.getAge()>30;}).collect(Collectors.toList());
// 先排序筛选年龄大于30的
List<User> list1 = list.stream().sorted((user1,user2)->{
return user1.getAge()-user2.getAge();
}).filter(user -> {return user.getAge()>30;}).collect(Collectors.toList());
遍历 看起来有点像js的写法list2.forEach((user)->{
if(user.getAge()>60)
{
tempList.add(user);
System.out.println("test:"+user);
}
});//遍历 实际两种差不多 第二种更偏向于改原来的值 第一种就纯粹是一个便利操作了
//映射每个用户对应年龄加一
list.stream().map(user -> {user.setAge(user.getAge()+1);
return user;
});
计算数量 类型为long
long num = list.stream().count();
Optional<User> max =list.stream().max((user1, user2)-> { //通过max 取最大值
return user1.getAge()-user2.getAge();
});
User maxuser =list.stream().max((user1, user2)-> {// 通过max 取最大值 通过get 获得age最大的那个对象
return user1.getAge()-user2.getAge();
}).get();
// 过滤判断
// 获得 属性集合
List<user> notNullList = orderDetailList.stream().filter(x -> !StringUtils.isEmpty(x.getCyitemno())).collect(Collectors.toList());
List<String> cyitemnolist = notNullList.stream().map(x -> x.getCyitemno()).collect(Collectors.toList())