Java8新特性 StreamAPI实例详解

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俱乐部其它相关文章!

本文收集自网络,不代表IT俱乐部立场,转载请注明出处。https://www.2it.club/code/java/4266.html
上一篇
下一篇
联系我们

联系我们

在线咨询: QQ交谈

邮箱: 1120393934@qq.com

工作时间:周一至周五,9:00-17:30,节假日休息

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部