变换流元素

Java 9并发编程指南 目录

作用在流上最重要的一些中间操作是对流元素进行转换。这些操作接收一个类的元素并且返回不同类的元素,甚至能够改变流的类型,从Stream生成IntStream、LongStream或者DoubleStream。

本节中,学习如何使用Stream类的转换中间操作将元素变成其它类的元素。

准备工作

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

实现过程

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

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

  2. 创建名为BasicPerson的类,包括名为name的String属性和age的long属性。创建这两个属性的get()和set()方法,代码很简单,不在此列出。

  3. 创建附属类FIleGenerator,包括名为generateFile()方法,用来接收模拟文件的行数,并将文件对应内容作为String列表返回:

    public class FileGenerator {
    	public static List<String> generateFile(int size) {
    		List<String> file = new ArrayList<>();
    		for (int i=0; i<size; i++) {
    			file.add("Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
    					+ " Morbi lobortis cursus venenatis. "
    					+ " Mauris tempus elit ut malesuada luctus."
    					+ " Interdum et malesuada fames ac ante ipsum primis in faucibus. "
    					+ " Phasellus laoreet sapien eu pulvinar rhoncus. "
    					+ " Integer vel ultricies leo. Donec vel sagittis nibh."
    					+ " Maecenas eu quam non est hendrerit pu");
    		}
    		return file;
    	}
    }
    
  4. 然后,创建包括main()方法的Main类。首先,使用PersonGenerator类创建随机的Person对象列表:

    public class Main {
    	public static void main(String[] args) {
    		List<Person> persons = PersonGenerator.generatePersonList(100);
    
  5. 然后,使用mapToDouble()方法将Person对象流转换成双精度数字的DoubleStream。使用parallelStream ()方法创建并行流,然后使用mapToDouble()方法,将lambda表达式作为参数传递,此表达式接收Person对象并返回其双精度的工资数。 接着使用distinct()方法得到重复值,以及forEach()方法将它们输出到控制台。使用count()方法还得到输出不同元素的数量:

    		DoubleStream ds = persons.parallelStream().mapToDouble(p -> p.getSalary());
    		ds.distinct().forEach(d -> {
    			System.out.printf("Salary: %f\n", d);
    		});
    		ds = persons.parallelStream().mapToDouble(p -> p.getSalary());
    		long size = ds.distinct().count();
    		System.out.printf("Size: %d\n", size);
    
  6. 现在,将流的Person对象转换成BasicPerson对象。使用parallelStream()创建流,以及map()方法转换对象。此方法将接收Person对象的lambda表达式作为参数接收,创建新的BasicPerson对象并赋值。接下来,使用forEach()方法输出BasicPerson对象的属性值到控制台:

    		List<BasicPerson> basicPersons = persons.parallelStream().map(p -> {
    			BasicPerson bp = new BasicPerson();
    			bp.setName(p.getFirstName() + " " + p.getLastName());
    			bp.setAge(getAge(p.getBirthDate()));
    			return bp;
    		}).collect(Collectors.toList());
    		
    		basicPersons.forEach(bp -> {
    			System.out.printf("%s: %d\n", bp.getName(), bp.getAge());
    		});
    
  7. 接下来,我们学习如何管理中间操作返回Steam的情况。 本范例中,将使用流的Stream,但使用flatMap()方法将所有Stream对象连接到唯一的Stream中。 使用FileGenerator类生成包含100个元素的List,然后使用parallelStream()方法创建并行流,使用split()方法拆分每行来得到词语,并且使用Stream类的of()方法,将Array结果装换成Stream。如果使用map()方法,将生成流的Stream,但使用flatMap()方法得到包含整个列表的所有单词的字符串对象的唯一Stream。 然后,使用filter()方法获得长度大于零的词语,使用sorted()方法排序流,使用groupingByConcurrent()方法将流集合成Map,其中键是词语,值为每个词在流中出现的次数:

    		List<String> file = FileGenerator.generateFile(100);
    		Map<String, Long> wordCount = file.parallelStream()
    			.flatMap(line -> Stream.of(line.split("[ ,.]")))
    			.filter(w -> w.length() > 0).sorted()
    			.collect(Collectors.groupingByConcurrent(e -> e, Collectors
    			.counting()));
    		wordCount.forEach((k, v) -> {
    			System.out.printf("%s: %d\n", k, v);
    		});
    	}
    
  8. 最后,需要实现之前用到过的getAge()方法,此方法接收Person对象的生日,返回其年龄:

    	private static long getAge(Date birthDate) {
    		LocalDate start = birthDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
    		LocalDate now = LocalDate.now();
    		long ret = ChronoUnit.YEARS.between(start, now);
    		return ret;
    	}
    

工作原理

本节学习如何使用中间操作和表达式转换流元素,在流和目标类型之间实现转换。本范例中用到三个不同的方法:

  • mapToDouble():使用此方法把Stream对象转换成双精度数字元素的DoubleStream。此方法将lambda参数或者ToDoubleFunction接口实现作为参数接收,这个表达式接收Stream元素,且需要返回双精度数。
  • map():当需要将Stream元素转成不同类时,使用此方法。例如本范例中,将Person类转换成BasicPerson类。此方法将lambda参数或者Function接口实现作为参数接收,这个表达式必须创建新的对象并且初始化其属性。
  • flatMap():当需要将Stream对象的流转换成唯一Stream时,对于这种复杂的情况使用此方法更有效。此方法将lambda表达式作为参数,或者Function接口实现作为map()函数接收,但这种情况下,此表达式需要返回Stream对象。flatMap()方法将自动把所有流连接成一个唯一的Stream。

扩展学习

Stream类还提供转换Stream元素的其它方法:

  • mapToInt(),mapToLong():这些方法mapToDouble()方法相同,只是它们分别生成IntStream和LongStream对象。
  • flatMapToDouble(),flatMapToInt(),flatMapToLong():这些方法与flatMap()方法相同,不过分别作用到DoubleStream、IntStream和LongStream。

更多关注

  • 本章“创建不同来源的流”、“归约流元素”和“集合流元素”小节
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值