Jim Blog I've been trying so hard,but doesn't even matter
博客信息

Java8-Stream的初步学习

发布时间:『 2018-03-25 17:58 』  博客类别:Java核心基础  阅读(88) 评论(0)

StreamJava8中新增的接口,你可以利用它处理集合里的数据,虽然对于Stream的性能目前还是有不少的争议,但Stream语法的简便却是不可置疑的。


流的获取:

集合中可以产生顺序(sequentialStream和并行(parallelStream,区别在于前者是单线程运行而后者是多线程运行,它们的获取方式分别为:

List<String> list=Arrays.asList("b","a","f","c","d","a");
           list.stream();          //顺序流
           list.parallelStream();     //并行流


外部迭代和内部迭代:

外部迭代即显式的进行迭代并对数据进行操作,for循环和iterator都是外部迭代

内部迭代即迭代在集合内部进行,我们只需要定义对元素进行的操作即可,在初次学习Lambda文章中的list.forEach()也是内部迭代。

比如我们要对上面的list集合求出有多少个”a”,外部迭代的代码为:

int count=0;
           for (String string : list) {
                if(Objects.equals(string, "a")){
                      count++;
                }
           }

而使用Stream的内部迭代代码为:

int count=0;
           count=(int) list.stream().filter(x->Objects.equals(x, "a")).count();
           System.out.println(count);

由于代码很简单,所以感觉没什么区别,可如果for循环内的操作很复杂或者嵌套循环,外部迭代的方式则会模糊了代码的本意,别人则需要阅读整个循环体才知道你到底做了什么。Stream则遵守“做什么,而不是怎么去做”的原则。

在讲流的操作前,我们应该知道几点:

1.流不改变它们的源数据

2.如果流的操作返回的是还是一个流的话,我们称其为惰性求值(这种操作也被称处理操作或中间操作)。如果返回值是空或是任一值,则称为及早求值(这种操作则被称为聚合操作)。


常用的流操作:

1.collect(toList())

Stream里的值生成一个列表,返回的是list,所以是及早求值:

List<String> collect = list.stream().collect(Collectors.toList());
           System.out.println(collect);

2.filter

顾名思义,即对Stream的值进行过滤,由于其返回一个流,我们需要使用其他方法使其返回值,这里我选用了返回列表的方法:

List<String> collect = list.stream().filter(x->x.equals("b")).collect(Collectors.toList());
           System.out.println(collect);

3.map

由映射元素到其对应的结果,一般用于将某种类型的值换成另外一种类型,例如我们要将集合list的值换成大写:

List<String> collect = list.stream().map(x->x.toUpperCase()).collect(Collectors.toList());
           System.out.println(collect);

4.maxmin

即求最大最小值,在方法里用lambda表达是写出规则,例如下面求最大值代码:

List<Integer> list=Arrays.asList(1,2,3,4,5);
           int max = list.stream().max((x,y)->x-y).get();
           System.out.println(max);

5.sorted

默认会进行字典排序,也可自行写排序规则:

List<String> collect = list.stream().sorted().collect(Collectors.toList());
           System.out.println(collect);

6.forEach

由于集合中已经增加了forEach方法,单纯为了遍历而使用这个方法并无意义,但配合其他方法进行操作后进行遍历则方便不少,例如前面将操作后的结果都转换成列表然后再输出的可以写成:

list.stream().sorted().forEach(System.out::print);


Stream还有很多其他的用法,我也不可能一一赘述,更为高深的用法我也将持续学习。


Stream的性能:

使用Stream可以使代码的可读性有质的提升,但由于其效率的问题很多人不敢向其转变,我也用一些代码测试了一下运行时间的差异:

public static void main(String[] args) {
           List<String> list=new ArrayList<>(); 
           for(int i=0;i<1000000;i++){
                list.add(i+"");
           }
           
           long before1=System.currentTimeMillis();
           for (String s : list) {
                System.out.println(s);
           }
           long after1=System.currentTimeMillis();
           
           long before2=System.currentTimeMillis();
           list.forEach(System.out::println);
           long after2=System.currentTimeMillis();
           
           long before3=System.currentTimeMillis();
           list.stream().forEach(System.out::println);
           long after3=System.currentTimeMillis();
           
           long time1=after1-before1;
           long time2=after2-before2;
           long time3=after3-before3;
           
           System.out.println("传统forEach:"+time1);
           System.out.println("list中新增forEach:"+time2);
           System.out.println("Stream中forEach:"+time3);
     }

使用String是为了防止其他类型自动装箱拆箱造成的时间差异,运行这段代码应该要先注释掉其他两个方法,根据我的测试放在前面的方法会比放在后面的方法慢一点,应该是JVM在后台进行了优化,测试结果确实如网上所说Stream最慢,但差异并没网上说得大,不超过1s,在提高一个数量级后(1千万),Stream反而快了将近1s。如果测试的代码有什么不周到的,欢迎指出问题。


关键字:   Java     Stream  
博主信息
Jim
(生命,是一场徒劳的坚持)