继续java8源码的发烧热,越看越是有充实的感觉。
数据时代下的产物
Java顺应时代的发展推出的高效处理大量数据能力的api,它专注于对集合对象进行各种非常便利、高效的聚合操作,借助于同样新出现的 Lambda 表达式,极大的提高编程效率和程序可读性。同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,可以很方便地写出高性能的并发程序。
Stream可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
核心概念理解
一个流的操作 = 创建流 + 中间操作 + 结果转换。
- 创建流:指从集合转换过滤,数值转换过滤、I/O转换、创建等等;
- 中间操作:指对流中的数据进行聚合,如filter\map\sorted\reduce、distinct等等;
- 结果转换:指将流进行输出,打印、转换成array、转换成collection等;
Stream重要点:
- 流是不存储值的,只是一个管道过程
- 单个流只能被使用一次,再次被使用报错
- 流是支持函数式调用
测试样例定义
static Listcars = new ArrayList<>();static Map kv = new HashMap<>();public static void init(){ for(int i = 1; i <= 10; i++){ WeiLaiCar car = new WeiLaiCar(); car.setCarWheel(i); cars.add(car); kv.put(i,i+"-value"); }}
源码深度
在java.util.stream目录下可以看出有多种stream可以使用。Stream是支持泛型的对象的流,另外还提供了double\int\long常用数据类型的流,但他们提供的方法基本类似。
1、流是管道内部单向不可逆操作
Random random = new Random();IntStream intStream = random.ints(10);intStream.limit(6).forEach(PrintUtil::printTest);intStream.toArray();
这里的第二个intStream.toArray()执行会报错误:IllegalStateException: stream has already been operated upon or closed
,流创建流到结束流单个管道内只能顺序一次使用,想要再次使用就得从源头重新创建流重新开始;
Random random = new Random();IntStream intStream = random.ints(10);intStream.limit(6).forEach(PrintUtil::printTest);random.ints(15).toArray();
2、Stream的串行和并行
流的计算提供了两种模式:串行流和并行流,认是创建串行流。
- 提供流方法parallel()和sequential()在串并流间相互转换。
- 在Collection接口上添加了两个方法stream()\parallelStream(),parallel是并行计算的。这两个方法的内部实现皆是通过StreamSupport.stream(xx,aa)来实现,该方法的aa参数是boolean代表是否开启并行流;
3、collection与stream转换
流提供有方法toArray()转换成数组,也提供了方法collect()转换为list\set\Collection集合;同时Arrays.stream()方法可以把数组转换为流。
//list -> streamStreamcarStream = cars.parallelStream();carStream.filter(a -> a.getWheelCount() > 5).map(a-> a.hashCode()+"|"+a.getWheelCount()).forEach(PrintUtil::printTest);//list转换为并发stream并打印符合条件的对象的hashcode和wheel数量:730140433|8//stream -> listStream tmp = cars.stream();List carList = tmp.collect(Collectors.toList());carList.forEach(PrintUtil::printTest);//通过collect方法把stream转换为list并打印对象全称:com.ts.util.optional.WeiLaiCar@4e515669//array -> streamICar[] carss = {new WeiLaiCar(1),new WeiLaiCar(2)};Arrays.stream(carss).filter(a -> a.getWheelCount() >2).forEach(PrintUtil::printTest);//最终输出8个wheel大于2个的对象,并打印对象全称:com.ts.util.optional.WeiLaiCar@4dcbadb4//stream -> arrayObject[] tmps = cars.stream().filter(a ->a.getWheelCount()>7).toArray();PrintUtil.printTest(tmps.length);//从cars中转换stream选出wheel大于7个的,转换为数值最终输出数量为:3
4、stream的排序
默认的排序要求排序对象是自然的有些值,一般dubble\int\long等;其他的类型需要主动定义comparator接口比较逻辑。
//默认sorted()要求是值natural order,即是自然顺序的cars.stream().map(a->a.getWheelCount()).sorted().forEach(PrintUtil::printTest);//先将对象的int类型的wheel聚合成集合,再使用默认排序(顺序)进行打印:1 2 3 4 5 6 ...10//自定义comparator,也可以在对象之间实现Comparator接口cars.stream().sorted(Comparator.comparingInt(ICar::getWheelCount)).map(a->a.getWheelCount()).forEach(PrintUtil::printTest);//自定义使用ICar的int类型wheel字段顺序比较,之后再将对象的wheel字段聚合进行打印:1 2 3 4 5 6 ...10cars.stream().sorted(Comparator.comparingInt(ICar::getWheelCount).reversed()).map(a->a.getWheelCount()).forEach(PrintUtil::printTest);//自定义使用ICar的int类型wheel字段顺序比较后再逆转,之后再将对象的wheel字段聚合进行打印//与上面的顺序正好相反:10 9 8 7 ... 1
流包下重要的工具类
- Collectors 提供了很多 reduction 操作,都是静态的方法,如聚合成集合或者其他类型,官网例子如下:
// Accumulate names into a ListListlist = people.stream().map(Person::getName).collect(Collectors.toList());// Accumulate names into a TreeSetSet set = people.stream().map(Person::getName).collect(Collectors.toCollection(TreeSet::new));// Convert elements to strings and concatenate them, separated by commasString joined = things.stream().map(Object::toString).collect(Collectors.joining(", "));// Compute sum of salaries of employeeint total = employees.stream().collect(Collectors.summingInt(Employee::getSalary)));// Group employees by departmentMap > byDept = employees.stream() .collect(Collectors.groupingBy(Employee::getDepartment));// Compute sum of salaries by departmentMap totalByDept = employees.stream() .collect(Collectors.groupingBy(Employee::getDepartment, Collectors.summingInt(Employee::getSalary)));// Partition students into passing and failingMap > passingFailing = students.stream() .collect(Collectors.partitioningBy(s -> s.getGrade() >= PASS_THRESHOLD));
- StreamSupport 是stream的底层创建操作方法实现类,让我们查看一下Stream类的部分方法源码:
/** * Returns an empty sequential {@code Stream}. * * @paramthe type of stream elements * @return an empty sequential stream */ public static Stream empty() { return StreamSupport.stream(Spliterators. emptySpliterator(), false); } /** * Returns a sequential {@code Stream} containing a single element. * * @param t the single element * @param the type of stream elements * @return a singleton sequential stream */ public static Stream of(T t) { return StreamSupport.stream(new Streams.StreamBuilderImpl<>(t), false); }
- 其他创建stream的方式,通过Stream的静态方法或者StreamSupport类:
StreamstringStream = Stream.of("1","a","c");stringStream.forEach(PrintUtil::printTest);// 1 a cStream integerStream = Stream.empty();PrintUtil.printTest("count is " + integerStream.count());//count is 0double[] tm = {22D,1.11D,33D,20.12D,11.02D,9.34D};DoubleStream doubleStream = StreamSupport.doubleStream(Spliterators.spliterator(tm,0,5,1),true);doubleStream.filter(a -> a < 20).forEach(PrintUtil::printTest);//1.11 11.02
要主动拥抱新技术的出现
看着公司年轻的工程师一个比一个年轻,我变的更加努力了。要鞭策自己在项目中多尝试使用新API各种类和方法以锻炼自己,这些东西就是需要多用你才能发现区别并提升你的技术能力。
就算是写业务的也能写出不一样的水平。时常告诉自己去学习,追求进步不要做茫茫的小白。 对于一个工程师来说学习能力是你在这个行业必备的能力。
测试用例地址:
作者:Owen Jia
欢迎关注他的博客:
坚持写博客成了我的一种寄托