排序流元素

Java 9并发编程指南 目录

排序流元素是Stream的典型操作。例如,需要按照名称、邮编,或者其它任何数字值对Stream元素进行排序。

对于流,我们还需要考虑所谓的出现顺序。 一些流可能有一个定义好的出现顺序(这取决于Stream的来源)。有些操作使用出现顺序中的流元素,例如limit()、skip()和其他操作,使得这些方法的并行计算性能很差。这种情况下,通过删除排序约束,可以加速方法执行时间。

本节将学习如何排序Stream元素,以及在不需要Stream出现顺序的情况下,如何删除排序约束。

准备工作

本范例通过Eclipse开发工具实现。如果使用诸如NetBeans的开发工具,打开并创建一个新的Java项目。

实现过程

在本节中,将实现之前描述的如何通过输入源创建流的范例。通过如下步骤实现范例:

  1. 首先,创建本范例中用到的辅助类。实现Person类存储人员基本属性,PersonGenerator类生成随机Person对象列表。请查看小节“应用操作到流的每个元素”中这两个类的源码。

  2. 现在,创建包括main()方法的Main类。首先,创建int数字数组,使用这个数组中的parallelStream()方法创建并行流,使用sort()方法对数组中的元素进行排序,并使用forEachOrdered()方法按照顺序输出元素。因为此操作需要按照指定的顺序来输出元素,所以无法使用多核处理器的所有性能:

    public class Main {
    	public static void main(String[] args) {
    		int[] numbers={9,8,7,6,5,4,3,2,1,2,3,4,5,6,7,8,9};
    		Arrays.stream(numbers).parallel().sorted().forEachOrdered(n -> {
    			System.out.printf("%d\n", n);
    		});
    
  3. 现在,使用Person对象的Stream尝试相同的操作,使用PersonGenerator类创建10个随机Person对象列表,并且使用相同的sorted()和forEachOrdered()方法,查看人员如何按照顺序输出:

    		List<Person> persons=PersonGenerator.generatePersonList(10);
    		persons.parallelStream().sorted().forEachOrdered(p -> {
    			System.out.printf("%s, %s\n",p.getLastName(),p.getFirstName());
    		});
    
  4. 最后查看如何使用unordered()方法打乱数据结构的出现顺序。首先,从随机Person对象的列表中创建TreeSet,使用TreeSet是因为它在内部排序元素。然后使用循环重复10次操作来看排序和非排序操作之间有什么区别:

    		TreeSet<Person> personSet=new TreeSet<>(persons);
    		for (int i=0; i<10; i++) {
    
  5. 然后,使用stream()方法创建流,parallel()方法将其转换成并行流,使用limit()方法得到第一个元素且返回Person对象,集合成列表并得到第一个元素:

    		Person person= personSet.stream().parallel().limit(1).collect(Collectors.toList()).get(0);
    			System.out.printf("%s %s\n", person.getFirstName(),person.getLastName());
    
  6. 现在,执行相同的操作,但在stream()和parallel()方法之间使用unordered()删除排序约束:

    			person=personSet.stream().unordered().parallel().limit(1).collect(Collectors.toList()).get(0);
    			System.out.printf("%s %s\n", person.getFirstName(), person.getLastName());
    		}
    	}
    }
    

工作原理

因为流的来源以及应用到流上的中间操作,使得Stream对象会存在出现顺序。在元素必须通过特定方法处理时,出现顺序会利用此限制进行排序。例如,如果在流中按照出现顺序使用limit()或者skip()方法,考虑到出现顺序将获得和忽略第一个元素。还有其他操作,例如forEach()方法,就不需要考虑出现顺序。如果按照出现顺序对流应用相同操作,结果也是相同的。如果流没有出现顺序,结果可能是变化的。

当使用顺序流时,出现顺序对应用性能没有任何影响,但是并行流会对性能产生很大影响。根据操作不同,需要多次处理流元素,或者在缓冲区中存储大量数据。这种情况下,如本节中所用到的,使用unordered()方法删除出现顺序,会显著提升应用性能。

另一方面,sorted()方法对流元素进行排序,如果使用此方法,流元素必须实现Comparable接口。否则可以将用来排序元素的comparator作为参数传递,如果您使用此方法,您将创建一个有序流,因此,之前讲解的所有具有出现顺序的流都适用于合成流。

最后,forEach()方法不考虑流的出现顺序,如果要考虑出现顺序,例如排序后按顺序写入流元素 ,可以使用forEachOrdered()方法。

下图显示本范例在控制台输出的执行信息:

pics/06_04.jpg

可以看到当在生成自TreeSet的并行流中调用limit(1)时,因为流API遵循结构的出现顺序,所以通常会得到相同的结果。但是当调用unordered()方法时,则不考虑出现顺序,且得到的结果是可变的,如本范例所示。

扩展学习

当使用unordered()方法时,任何在数据结构中内部改变的元素顺序的代码都未执行,只是删除可能将某些方法考虑在内的一个条件。使用unordered()方法的流结果与为使用此方法的流结果可能是相同的,其用法可能会对并行流产生不同的处理结果。 例如,如果在本范例中使用Person对象列表代替TreeSet的personSet,两种情况下的均能得到相同的结果。

如前所述,unordered()方法的主要目的是删除制约并行流性能的排序约束。

更多关注

  • 本章“创建不同来源的流”、“归约流元素”和“集合流元素”小节
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值