5、并行流与并行排序
Java 8中,可以在接口不变的情况下,将流改为并行流。这样,就可以很自然地使用多线程进行集合中的数据处理。
5.1、使用并行流过滤数据
现在让我们考虑这么一个简单的案例,我们希望可以统计1~1000000内所有的质数的数量。首先,我们需要一个判断质数的函数:
public class PrimeUtil {
public static boolean isPrime(int number){
int temp = number;
if(temp<2){
return false;
}
for(int i = 2;Math.sqrt(temp)>=i;i++){
System.out.println(Math.sqrt(temp));
if(temp % i==0){
return false;
}
}
return true;
}
public static void main(String[] args) {
IntStream.range(1,15).filter(PrimeUtil::isPrime).forEach(System.out::println);
}
}
上述函数给定一个数字,如果这个数字是质数就返回true,否则返回false。 接着,使用函数式编程统计给定范围内所有的质数:
IntStream.range(1, 1000000).filter(PrimeUtil::isPrime).count();
上述代码首先生成一个1到1000000的数字流。接着使用过滤函数,只选择所有的质数,最后进行数量统计。
上述代码是串行的,将它改造成并行计算非常简单,只需要将流并行化即可:
IntStream.range(1, 1000000).parallel().filter(PrimeUtil::isPrime).count();
上述代码中,首先parallel()方法得到一个并行流,接着,在并行流上进行过滤,此时,PrimeUtil.isPrime()函数会被多线程并发调用,应用于流中的所有元素。
5.2、从集合到并行流
在函数式编程中,我们可以从集合得到一个流或者并行流。下面这段代码试图统计集合内所有学生的平均分:
List<Student> ss=new ArrayList<Student>();
double ave=ss.stream().mapToInt(s->s.score).average().getAsDouble();
从集合对象List中,我们使用stream()方法可以得到一个流。如果希望将这段代码并行化,则可以使用parallelStream()函数。
double ave=ss.parallelStream().mapToInt(s->s.score).average().getAsDouble();
可以看到,将原有的串行方式改造成并行执行是非常容易的。
5.3 并行排序
除了并行流外,对于普通数组,Java 8中也提供了简单的并行功能。比如,对于数组排序,我们有Arrays.sort()方法。当然这是串行排序,但在Java 8中,我们可以使用新增的Arrays. parallelSort()方法直接使用并行排序。 比如,你可以这样使用:
int[] arr=new int[10000000];
Random random = new Random();
Arrays.setAll(arr,x->random.nextInt());
Arrays.stream(arr).forEach(System.out::println);
除了并行排序外,Arrays中还增加了一些API用于数组中数据的赋值,比如:
public static void setAll(int[] array, IntUnaryOperator generator)
这是一个函数式味道很浓的接口,它的第2个参数是一个函数式接口。如果我们想给数组中每一个元素都附上一个随机值,则可以这么做:
Random r=new Random();
Arrays.setAll(arr, (i)->r.nextInt());
当然,以上过程是串行的。但是只要使用setAll()对应的并行版本,你就可以很快将它执行在多个CPU上:
Random r=new Random();
Arrays.parallelSetAll (arr, (i)->r.nextInt());