目录
Stream结果收集
结果收集到集合中结果收集到数组中对流中的数据做聚合计算对流中数据做分组操作对流中的数据做分区操作对流中的数据做拼接
并行的Stream流
串行的Stream流并行流
并行流和串行流对比线程安全问题
Stream结果收集
面试官:说说你常用的StreamAPI。
结果收集到集合中
- public static void main(String[] args){
- // Stream<String> stream = Stream.of("aa", "bb", "cc");
- List<String> list = Stream.of("aa", "bb", "cc","aa")
- .collect(Collectors.toList());
- System.out.println(list);
- // 收集到 Set集合中
- Set<String> set = Stream.of("aa", "bb", "cc", "aa")
- .collect(Collectors.toSet());
- System.out.println(set);
- // 如果需要获取的类型为具体的实现,比如:ArrayList HashSet
- ArrayList<String> arrayList = Stream.of("aa", "bb", "cc", "aa")
- //.collect(Collectors.toCollection(() -> new ArrayList<>()));
- .collect(Collectors.toCollection(ArrayList::new));
- System.out.println(arrayList);
- HashSet<String> hashSet = Stream.of("aa", "bb", "cc", "aa")
- .collect(Collectors.toCollection(HashSet::new));
- System.out.println(hashSet);
- }
复制代码输出:
[aa, bb, cc, aa]
[aa, bb, cc]
[aa, bb, cc, aa]
[aa, bb, cc]
结果收集到数组中
Stream中提供了toArray方法来将结果放到一个数组中,返回值类型是Object[],如果我们要指定返回的类型,那么可以使用另一个重载的toArray(IntFunction f)方法。 - public static void main(String[] args){
- Object[] objects = Stream.of("aa", "bb", "cc", "aa")
- .toArray(); // 返回的数组中的元素是 Object类型
- System.out.println(Arrays.toString(objects));
- // 如果我们需要指定返回的数组中的元素类型
- String[] strings = Stream.of("aa", "bb", "cc", "aa")
- .toArray(String[]::new);
- System.out.println(Arrays.toString(strings));
- }
复制代码 对流中的数据做聚合计算
当我们使用Stream流处理数据后,可以像数据库的聚合函数一样对某个字段进行操作,比如获得最大值,最小值,求和,平均值,统计数量。 - public static void main(String[] args) {
- // 获取年龄的最大值
- Optional<Person> maxAge = Stream.of(
- new Person("张三", 18)
- , new Person("李四", 22)
- , new Person("张三", 13)
- , new Person("王五", 15)
- , new Person("张三", 19)
- ).collect(Collectors.maxBy((p1, p2) -> p1.getAge() - p2.getAge()));
- System.out.println("最大年龄:" + maxAge.get());
- // 获取年龄的最小值
- Optional<Person> minAge = Stream.of(
- new Person("张三", 18)
- , new Person("李四", 22)
- , new Person("张三", 13)
- , new Person("王五", 15)
- , new Person("张三", 19)
- ).collect(Collectors.minBy((p1, p2) -> p1.getAge() - p2.getAge()));
- System.out.println("最新年龄:" + minAge.get());
- // 求所有人的年龄之和
- Integer sumAge = Stream.of(
- new Person("张三", 18)
- , new Person("李四", 22)
- , new Person("张三", 13)
- , new Person("王五", 15)
- , new Person("张三", 19)
- )
- //.collect(Collectors.summingInt(s -> s.getAge()))
- .collect(Collectors.summingInt(Person::getAge))
- ;
- System.out.println("年龄总和:" + sumAge);
- // 年龄的平均值
- Double avgAge = Stream.of(
- new Person("张三", 18)
- , new Person("李四", 22)
- , new Person("张三", 13)
- , new Person("王五", 15)
- , new Person("张三", 19)
- ).collect(Collectors.averagingInt(Person::getAge));
- System.out.println("年龄的平均值:" + avgAge);
- // 统计数量
- Long count = Stream.of(
- new Person("张三", 18)
- , new Person("李四", 22)
- , new Person("张三", 13)
- , new Person("王五", 15)
- , new Person("张三", 19)
- ).filter(p->p.getAge() > 18)
- .collect(Collectors.counting());
- System.out.println("满足条件的记录数:" + count);
- }
复制代码 对流中数据做分组操作
当我们使用Stream流处理数据后,可以根据某个属性将数据分组。 - public static void main(String[] args){
- // 根据账号对数据进行分组
- Map<String, List<Person>> map1 = Stream.of(
- new Person("张三", 18, 175)
- , new Person("李四", 22, 177)
- , new Person("张三", 14, 165)
- , new Person("李四", 15, 166)
- , new Person("张三", 19, 182)
- ).collect(Collectors.groupingBy(Person::getName));
- map1.forEach((k,v)-> System.out.println("k=" + k +"\t"+ "v=" + v));
- System.out.println("-----------");
- // 根据年龄分组 如果大于等于18 成年否则未成年
- Map<String, List<Person>> map2 = Stream.of(
- new Person("张三", 18, 175)
- , new Person("李四", 22, 177)
- , new Person("张三", 14, 165)
- , new Person("李四", 15, 166)
- , new Person("张三", 19, 182)
- ).collect(Collectors.groupingBy(p -> p.getAge() >= 18 ? "成年" : "未成年"));
- map2.forEach((k,v)-> System.out.println("k=" + k +"\t"+ "v=" + v));
- }
复制代码输出结果:
k=李四 v=[Person{name='李四', age=22, height=177}, Person{name='李四', age=15, height=166}]
k=张三 v=[Person{name='张三', age=18, height=175}, Person{name='张三', age=14, height=165}, Person{name='张三', age=19, height=182}]
-----------
k=未成年 v=[Person{name='张三', age=14, height=165}, Person{name='李四', age=15, height=166}]
k=成年 v=[Person{name='张三', age=18, height=175}, Person{name='李四', age=22, height=177}, Person{name='张三', age=19, height=182}]
多级分组: 先根据name分组然后根据年龄分组。 - public static void main(String[] args){
- // 先根据name分组,然后根据age(成年和未成年)分组
- Map<String,Map<Object,List<Person>>> map = Stream.of(
- new Person("张三", 18, 175)
- , new Person("李四", 22, 177)
- , new Person("张三", 14, 165)
- , new Person("李四", 15, 166)
- , new Person("张三", 19, 182)
- ).collect(Collectors.groupingBy(
- Person::getName
- ,Collectors.groupingBy(p->p.getAge()>=18?"成年":"未成年"
- )
- ));
- map.forEach((k,v)->{
- System.out.println(k);
- v.forEach((k1,v1)->{
- System.out.println("\t"+k1 + "=" + v1);
- });
- });
- }
复制代码输出结果:
李四
未成年=[Person{name='李四', age=15, height=166}]
成年=[Person{name='李四', age=22, height=177}]
张三
未成年=[Person{name='张三', age=14, height=165}]
成年=[Person{name='张三', age=18, height=175}, Person{name='张三', age=19, height=182}]
对流中的数据做分区操作
Collectors.partitioningBy会根据值是否为true,把集合中的数据分割为两个列表,一个true列表,一个false列表。 - public static void main(String[] args){
- Map<Boolean, List<Person>> map = Stream.of(
- new Person("张三", 18, 175)
- , new Person("李四", 22, 177)
- , new Person("张三", 14, 165)
- , new Person("李四", 15, 166)
- , new Person("张三", 19, 182)
- ).collect(Collectors.partitioningBy(p -> p.getAge() > 18));
- map.forEach((k,v)-> System.out.println(k+"\t" + v));
- }
复制代码输出结果:
false [Person{name='张三', age=18, height=175}, Person{name='张三', age=14, height=165}, Person{name='李四', age=15, height=166}]
true [Person{name='李四', age=22, height=177}, Person{name='张三', age=19, height=182}]
对流中的数据做拼接
Collectors.joining会根据指定的连接符,将所有的元素连接成一个字符串。 - public static void main(String[] args){
- String s1 = Stream.of(
- new Person("张三", 18, 175)
- , new Person("李四", 22, 177)
- , new Person("张三", 14, 165)
- , new Person("李四", 15, 166)
- , new Person("张三", 19, 182)
- ).map(Person::getName)
- .collect(Collectors.joining());
- // 张三李四张三李四张三
- System.out.println(s1);
- String s2 = Stream.of(
- new Person("张三", 18, 175)
- , new Person("李四", 22, 177)
- , new Person("张三", 14, 165)
- , new Person("李四", 15, 166)
- , new Person("张三", 19, 182)
- ).map(Person::getName)
- .collect(Collectors.joining("_"));
- // 张三_李四_张三_李四_张三
- System.out.println(s2);
- String s3 = Stream.of(
- new Person("张三", 18, 175)
- , new Person("李四", 22, 177)
- , new Person("张三", 14, 165)
- , new Person("李四", 15, 166)
- , new Person("张三", 19, 182)
- ).map(Person::getName)
- .collect(Collectors.joining("_", "###", "$$$"));
- // ###张三_李四_张三_李四_张三$$$
- System.out.println(s3);
- }
复制代码 并行的Stream流
串行的Stream流
我们前面使用的Stream流都是串行,也就是在一个线程上面执行。
并行流
parallelStream其实就是一个并行执行的流,它通过默认的ForkJoinPool,可以提高多线程任务的速度。
获取并行流
我们可以通过两种方式来获取并行流。
通过List接口中的parallelStream方法来获取通过已有的串行流转换为并行流(parallel)
- public static void main(String[] args){
- List<Integer> list = new ArrayList<>();
- // 通过List 接口 直接获取并行流
- Stream<Integer> integerStream = list.parallelStream();
- // 将已有的串行流转换为并行流
- Stream<Integer> parallel = Stream.of(1, 2, 3).parallel();
- }
复制代码 并行流操作
- public static void main(String[] args){
- Stream.of(1,4,2,6,1,5,9)
- .parallel() // 将流转换为并发流,Stream处理的时候就会通过多线程处理
- .filter(s->{
- System.out.println(Thread.currentThread() + " s=" +s);
- return s > 2;
- }).count();
- }
复制代码 并行流和串行流对比
我们通过for循环,串行Stream流,并行Stream流来对500000000亿个数字求和。来看消耗时间。 - public class Test {
- private static long times = 500000000;
- private long start;
- @Before
- public void befor(){
- start = System.currentTimeMillis();
- }
- @After
- public void end(){
- long end = System.currentTimeMillis();
- System.out.println("消耗时间:" + (end - start));
- }
- /**
- * 普通for循环 消耗时间:138
- */
- @Test
- public void test01(){
- System.out.println("普通for循环:");
- long res = 0;
- for (int i = 0; i < times; i++) {
- res += i;
- }
- }
- /**
- * 串行流处理
- * 消耗时间:203
- */
- @Test
- public void test02(){
- System.out.println("串行流:serialStream");
- LongStream.rangeClosed(0,times)
- .reduce(0,Long::sum);
- }
- /**
- * 并行流处理 消耗时间:84
- */
- @Test
- public void test03(){
- LongStream.rangeClosed(0,times)
- .parallel()
- .reduce(0,Long::sum);
- }
- }
复制代码通过案例我们可以看到parallelStream的效率是最高的。
Stream并行处理的过程会分而治之,也就是将一个大的任务切分成了多个小任务,这表示每个任务都是一个线程操作。
线程安全问题
在多线程的处理下,肯定会出现数据安全问题。如下: - @Test
- public void test(){
- List<Integer> list = new ArrayList<>();
- for (int i = 0; i < 1000; i++) {
- list.add(i);
- }
- System.out.println(list.size());
- List<Integer> listNew = new ArrayList<>();
- // 使用并行流来向集合中添加数据
- list.parallelStream()
- //.forEach(s->listNew.add(s));
- .forEach(listNew::add);
- System.out.println(listNew.size());// 839
- }
复制代码针对这个问题,我们的解决方案有哪些呢?
加同步锁使用线程安全的容器通过Stream中的toArray/collect操作
以上就是Java8新特性 StreamAPI实例详解的详细内容,更多关于Java8新特性 StreamAPI的资料请关注中国红客联盟其它相关文章! |