2019独角兽企业重金招聘Python工程师标准>>>
继续java8源码的发烧热,越看越是有充实的感觉。
数据时代下的产物
Java顺应时代的发展推出的高效处理大量数据能力的api,它专注于对集合对象进行各种非常便利、高效的聚合操作,借助于同样新出现的 Lambda 表达式,极大的提高编程效率和程序可读性。同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,可以很方便地写出高性能的并发程序。
Stream可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
核心概念理解
一个流的操作 = 创建流 + 中间操作 + 结果转换。
- 创建流:指从集合转换过滤,数值转换过滤、I/O转换、创建等等;
- 中间操作:指对流中的数据进行聚合,如filter\map\sorted\reduce、distinct等等;
- 结果转换:指将流进行输出,打印、转换成array、转换成collection等;
Stream重要点:
- 流是不存储值的,只是一个管道过程
- 单个流只能被使用一次,再次被使用报错
- 流是支持函数式调用
测试样例定义
static List<ICar> cars = new ArrayList<>();static Map<Integer,String> 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 -> stream
Stream<ICar> carStream = cars.parallelStream();
carStream.filter(a -> a.getWheelCount() > 5).map(a-> a.hashCode()+"|"+a.getWheelCount()).forEach(PrintUtil::printTest);
//list转换为并发stream并打印符合条件的对象的hashcode和wheel数量:730140433|8//stream -> list
Stream<ICar> tmp = cars.stream();
List<ICar> carList = tmp.collect(Collectors.toList());
carList.forEach(PrintUtil::printTest);
//通过collect方法把stream转换为list并打印对象全称:com.ts.util.optional.WeiLaiCar@4e515669//array -> stream
ICar[] 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 -> array
Object[] 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 ...10
cars.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 List
List<String> list = people.stream().map(Person::getName).collect(Collectors.toList());// Accumulate names into a TreeSet
Set<String> set = people.stream().map(Person::getName).collect(Collectors.toCollection(TreeSet::new));// Convert elements to strings and concatenate them, separated by commas
String joined = things.stream().map(Object::toString).collect(Collectors.joining(", "));// Compute sum of salaries of employee
int total = employees.stream().collect(Collectors.summingInt(Employee::getSalary)));// Group employees by department
Map<Department, List<Employee>> byDept = employees.stream().collect(Collectors.groupingBy(Employee::getDepartment));// Compute sum of salaries by department
Map<Department, Integer> totalByDept = employees.stream().collect(Collectors.groupingBy(Employee::getDepartment,Collectors.summingInt(Employee::getSalary)));// Partition students into passing and failing
Map<Boolean, List<Student>> passingFailing = students.stream().collect(Collectors.partitioningBy(s -> s.getGrade() >= PASS_THRESHOLD));
- StreamSupport 是stream的底层创建操作方法实现类,让我们查看一下Stream类的部分方法源码:
/*** Returns an empty sequential {@code Stream}.** @param <T> the type of stream elements* @return an empty sequential stream*/public static<T> Stream<T> empty() {return StreamSupport.stream(Spliterators.<T>emptySpliterator(), false);}/*** Returns a sequential {@code Stream} containing a single element.** @param t the single element* @param <T> the type of stream elements* @return a singleton sequential stream*/public static<T> Stream<T> of(T t) {return StreamSupport.stream(new Streams.StreamBuilderImpl<>(t), false);}
- 其他创建stream的方式,通过Stream的静态方法或者StreamSupport类:
Stream<String> stringStream = Stream.of("1","a","c");
stringStream.forEach(PrintUtil::printTest);
// 1 a cStream<Integer> 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各种类和方法以锻炼自己,这些东西就是需要多用你才能发现区别并提升你的技术能力。
就算是写业务的也能写出不一样的水平。时常告诉自己去学习,追求进步不要做茫茫的小白。 对于一个工程师来说学习能力是你在这个行业必备的能力。
测试用例地址:play-java-sample
作者:Owen Jia
欢迎关注他的博客:Owen Blog
坚持写博客成了我的一种寄托