Stream结果收集
面试官:说说你常用的StreamAPI。
结果收集到集合中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | public static void main(String[] args){ // Stream stream = Stream.of("aa", "bb", "cc"); List list = Stream.of( "aa" , "bb" , "cc" , "aa" ) .collect(Collectors.toList()); System.out.println(list); // 收集到 Set集合中 Set set = Stream.of( "aa" , "bb" , "cc" , "aa" ) .collect(Collectors.toSet()); System.out.println(set); // 如果需要获取的类型为具体的实现,比如:ArrayList HashSet ArrayList arrayList = Stream.of( "aa" , "bb" , "cc" , "aa" ) //.collect(Collectors.toCollection(() -> new ArrayList())); .collect(Collectors.toCollection(ArrayList:: new )); System.out.println(arrayList); HashSet 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)方法。
1 2 3 4 5 6 7 8 9 | 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流处理数据后,可以像数据库的聚合函数一样对某个字段进行操作,比如获得最大值,最小值,求和,平均值,统计数量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | public static void main(String[] args) { // 获取年龄的最大值 Optional 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 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流处理数据后,可以根据某个属性将数据分组。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | public static void main(String[] args){ // 根据账号对数据进行分组 Map> 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> 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分组然后根据年龄分组。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | public static void main(String[] args){ // 先根据name分组,然后根据age(成年和未成年)分组 Map>> 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列表。
1 2 3 4 5 6 7 8 9 10 | public static void main(String[] args){ Map> 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会根据指定的连接符,将所有的元素连接成一个字符串。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | 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)
1 2 3 4 5 6 7 | public static void main(String[] args){ List list = new ArrayList(); // 通过List 接口 直接获取并行流 Stream integerStream = list.parallelStream(); // 将已有的串行流转换为并行流 Stream parallel = Stream.of( 1 , 2 , 3 ).parallel(); } |
并行流操作
1 2 3 4 5 6 7 8 | 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亿个数字求和。来看消耗时间。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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 |
通过案例我们可以看到parallelStream的效率是最高的。
Stream并行处理的过程会分而治之,也就是将一个大的任务切分成了多个小任务,这表示每个任务都是一个线程操作。
线程安全问题
在多线程的处理下,肯定会出现数据安全问题。如下:
1 2 3 4 5 6 7 8 9 10 | @Test public void test(){ List list = new ArrayList(); for ( int i = 0 ; i 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的资料请关注IT俱乐部其它相关文章!