Stream API

1中间与最终方法

流作用是提供了一种操作大数据接口,让数据操作更容易和更快。它具有过滤、映射以及减少遍历数等方法,这些方法分两种:中间方法和终端方法,“流”抽象天生就该是持续的,中间方法永远返回的是Stream,因此如果我们要获取最终结果的话,必须使用最终操作才能收集流产生的最终结果。区分这两个方法是看他的返回值,如果是Stream则是中间方法,否则是终点方法。

·       Intermediate:

map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、sequential、 unordered

·       Terminal:

forEach、 forEachOrdered、 toArray、 reduce、collect、 min、 max、 count、 anyMatch、allMatch、 noneMatch、 findFirst、 findAny、 iterator


简单介绍下几个中间方法(filter、map)以及终点方法(collect、sum、reduce)

1.1Filter

在数据流中实现过滤功能是首先我们可以想到的最自然的操作了。Stream接口暴露了一个filter方法,它可以接受表示操作的Predicate实现来使用定义了过滤条件的lambda表达式。


List persons = …
Stream personsOver18 = persons.stream().filter(p -> p.getAge() > 18);//过滤18岁以上的人

1.2Map

假使我们现在过滤了一些数据,比如转换对象的时候。Map操作允许我们执行一个Function的实现(Function<T,R>的泛型T,R分别表示执行输入和执行结果),它接受入参并返回。

首先,让我们来看看怎样以匿名内部类的方式来描述它:

 

 

 

 

 

 

 

 

Stream adult= persons

              .stream()

              .filter(p -> p.getAge() > 18)

              .map(new Function() {

                  @Override

                  public Adult apply(Person person) {

                     return new Adult(person);//将大于18岁的人转为成年人

                  }

              });

现在,把上述例子转换成使用lambda表达式的写法:

 

 

Stream map = persons.stream()

                    .filter(p -> p.getAge() > 18)

                    .map(person -> new Adult(person));

 


1.3Count

count方法是一个流的终点方法,可使流的结果最终统计,返回int,比如我们计算一下满足18岁的总人数

 

 

 

int countOfAdult=persons.stream()

                       .filter(p -> p.getAge() > 18)

                       .map(person -> new Adult(person))

                       .count();

1.4Collect

collect方法也是一个流的终点方法,可收集最终的结果

 

 

 

List adultList= persons.stream()

                       .filter(p -> p.getAge() > 18)

                       .map(person -> new Adult(person))

                       .collect(Collectors.toList());

或者,如果我们想使用特定的实现类来收集结果:

 

 

 

 

List adultList = persons

                 .stream()

                 .filter(p -> p.getAge() > 18)

                 .map(person -> new Adult(person))

                 .collect(Collectors.toCollection(ArrayList::new));

 

1.5reduce

reduce方法的主要作用是把 Stream 元素组合起来。它提供一个起始值(种子),然后依照运算规则(BinaryOperator),和前面 Stream 的第一个、第二个、第 n 个元素组合。从这个意义上说,字符串拼接、数值的 sum、min、max、average 都是特殊的 reduce。

例如

// 求和

Integersum = integers.reduce(0, (a, b) -> a+b);

Integersum = integers.reduce(0, Integer::sum);

 

// 字符串连接,concat = "ABCD"

Stringconcat = Stream.of("A", "B", "C","D").reduce("", String::concat);

 

// 求最小值,minValue = -3.0

doubleminValue = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double.MAX_VALUE,Double::min);

/

/ 求和,sumValue = 10, 有起始值

intsumValue = Stream.of(1, 2, 3, 4).reduce(0, Integer::sum);

// 求和,sumValue = 10, 无起始值

sumValue= Stream.of(1, 2, 3, 4).reduce(Integer::sum).get();

 

也有没有起始值的情况,这时会把Stream 的前面两个元素组合起来,返回的是 Optional。

 

 

2顺序流与并行流

每个Stream都有两种模式:顺序执行和并行执行。

顺序流:

List <Person> people = list.getStream.collect(Collectors.toList());

并行流:

List <Person> people = list.getStream.parallel().collect(Collectors.toList());

顾名思义,当使用顺序方式去遍历时,每个item读完后再读下一个item。而使用并行去遍历时,数组会被分成多个段,其中每一个都在不同的线程中处理,然后将结果一起输出。

2.1并行流原理:

 

 

 

 

 

List originalList = someData;

split1 = originalList(0, mid);//将数据分小部分

split2 = originalList(mid,end);

new Runnable(split1.process());//小部分执行操作

new Runnable(split2.process());

List revisedList = split1 + split2;//将结果合并

Stream则是利用多核技术可将大数据通过多核并行处理。

2.2顺序与并行性能测试对比

如果是多核机器,理论上并行流则会比顺序流快上一倍,下面是测试代码

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

long t0 = System.nanoTime();

//初始化一个范围100万整数流,求能被2整除的数字,toArray()是终点方法

int a[]=IntStream.range(0, 1000000).filter(p -> p % 2==0).toArray();

  

long t1 = System.nanoTime();

  

//和上面功能一样,这里是用并行流来计算

int b[]=IntStream.range(0, 1000000).parallel().filter(p -> p % 2==0).toArray();

long t2 = System.nanoTime();

  

//我本机的结果是serial: 0.04s, parallel 0.03s,证明并行流确实比顺序流快

System.out.printf("serial: %.2fs, parallel %.2fs%n", (t1 - t0) * 1e-9, (t2 - t1) * 1e-9);



方法引用

其实是lambda表达式的一个简化写法,所引用的方法其实是lambda表达式的方法体实现,语法也很简单,左边是容器(可以是类名,实例名),中间是”::”,右边是相应的方法名(不需要写括号)。

格式有以下:

·      ClassName::new //构造方法

·      objectName::instanceMethod //对象的实例方法

·      ClassName::staticMethod //类的静态方法

·      ClassName::instanceMethod //特定类的实例方法

第一种是构造器引用,请注意构造器没有参数。

Person::new等同于new Person ()

中间两种方式类似,等同于把lambda表达式的参数直接当成instanceMethod|staticMethod的参数来调用。比如

Person::eat等同于person->person.eat();

System.out::println等同于x->System.out.println(x);

Math::max等同于(x,y)->Math.max(x,y)。

最后一种方式,某个类的实例方法,等同于把lambda表达式的第一个参数当成instanceMethod的目标对象,其他剩余参数当成该方法的参数。

比如String::toLowerCase等同于x->x.toLowerCase()。

String::compareTo 等同于(s1, s2)-> s1.compareTo(s2)

 

 




参考文章:

http://www.importnew.com/20331.html

http://www.ibm.com/developerworks/cn/java/j-lo-java8streamapi/index.html



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值