2019独角兽企业重金招聘Python工程师标准>>>

Stream是JAVA SE8提供的主要用于处理集合的接口,定义是“从支持数据处理操作的源生成的元素序列”。Stream减少了集合操作的复杂程度,同时也实现了“行为参数化”:让方法接受多种行为(或战略)作为参数,并在内部使用,来完成不同的行为,以灵活应变程序的需求变化。

新加入的Stream API给集合操作提供了很大的灵活性,让在JAVA中进行数据处理变得更简洁、优雅。

一个Stream实现类对象类似一个集合,允许改变和获取数据,但是和集合还是有所差别:

  1. Stream本身并不存储元素,元素根据需要产生
  2. Stream操作符不会改变源对象,相反会返回一个持有结果的新Stream
  3. Stream操作符可能是延迟执行的,即可能的等到需要结果的时候才执行

使用Stream不关心具体的循环步骤,即“怎么去做”而只关心“做什么”,一般遵循三个步骤:

  1. 创建一个Stream
  2. 在一个或多个步骤中,指定将一个初始Stream装换为另一个Stream 的中间操作
  3. 使用一个终止操作来产生一个结果。

在操作Stream的过程中,会大量用到Lambda表达式和函数式接口,常见的函数式接口有:

  1. java.util.function.Predicate<T>接口定义了一个名叫test的抽象方法,它接受泛型T对象,并返回一个boolean。
  2. java.util.function.Consumer<T>定义了一个名叫accept的抽象方法,它接受泛型T的对象,没有返回(void) 。
  3. java.util.function.Function<T, R>接口定义了一个叫作apply的方法,它接受一个泛型T的对象,并返回一个泛型R的对象。
  4. Supplier<T>具有唯一一个抽象方法叫作get,代表的函数描述符是()-> T。
  5. BiFunction<T, U, R>具有唯一一个抽象方法叫作apply,代表的函数描述符是(T,
    U) -> R。

在处理基本类型时,还有专门的IntPredicate等方法,避免拆箱装修带来的性能消耗。

下面介绍使用Stream的步骤:

创建Stream:

List<Integer> list = new ArrayList<>();//所有的集合对象都可以通过stream方法转化为Stream对象
Stream<Integer> stream = list.stream();
//parallelStream适用于并行的情况
Stream<Integer> stream2 = list.parallelStream();//数组也可以通过Stream.of方法转化为Stream对象
Integer[] integers = new Integer[]{1,2,3,4,5};
Stream<Integer> stream3 = Stream.of(integers);//也可以直接通过Stream.of方法构建Stream对象,省略声明数组的部分
Stream<String> stream4 = Stream.of("a","b","c");//构建空的Stream对象
Stream<String> stream5 = Stream.empty();

除了这些方法以外,还有比较特殊的构建方法,可以用来生成“无限长度的流”:

//generate(Supplier<T> s)方法,参数为Supplier接口的实现对象,Supplier是一个函数式接口
//构建一个含有常量值的Stream
Stream.generate(()->"echo");
//构建一个含有随机数字的Stream
Stream.generate(Math::random);
//iterate方法,参数为UnaryOperator(一元运算符)接口
//根据需要构建无限长度的序列:0,1,2,3,4,5...
Stream.iterate(BigInteger.ZERO,n->n.add(BigInteger.ONE));

其中iterate方法可以用来生成“一系列的值”。比如用iterate方法生成斐波纳契元组序列:

Stream.iterate(new int[]{0,1},ints -> new int[]{ints[1],ints[0]+ints[1]}).limit(20).forEach(t-> System.out.println(t[0]+","+t[1]));

JAVA SE8中的许多其他类也添加了提供Stream 的方法,比如:

//根据正则表达式来拆分字符串为Stream
Stream<String> words = Pattern.compile("[\\P{L}]+").splitAsStream("InputStream");

中间操作:

一、Stream转换

Stream转换为从一个Stream中读取数据,然后将转换后的数据写入到另一个新的Stream里面。

  1. 过滤器转换:filter方法
//filter方法的参数是一个Predicate<T>对象,需要返回一个boolean值//创建一个Stream
Stream<String> words = Stream.of("aadfasdf","bqwerqw","caa");//使用filter方法筛选长度大于3的字符串
Stream<String> longWords = words.filter(w -> w.length()>3);

2. 形式转换(对Stream中的每一个元素应用同一个函数,并将每一个元素返回的唯一结果写到新的Stream里):map方法

//map方法的参数是Function接口
Stream<String> words = Stream.of("adfa","adfa","zxcv","qewr","erte");
//map对Stream中的每一个元素进行指定的转化操作,这里把所有元素变成"a"
System.out.println(Arrays.toString(words.map(x->"a").toArray()));//Stream对象会在一串操作结束或被关闭,需要重新声明
words = Stream.of("adfa","adfa","zxcv","qewr","erte");
//将所有元素转为大写
System.out.println(Arrays.toString(words.map(String::toUpperCase).toArray()));

3. 形式装换多返回值(对Stream中的每一个元素应用同一个函数,并将每一个元素返回的多个结果写到新的Stream里):flatMap方法

//flatMap方法的参数是Function接口
Stream<String> words = Stream.of("adfa","adfa","zxcv","qewr","erte");
//把words拆成{"a""d""f"...}
//和map的差别在于,map会产生一个Stream<Stream<Character>>对象
Stream<Character> result =words.flatMap(new Function<String, Stream<? extends Character>>() {@Overridepublic Stream<? extends Character> apply(String s) {List<Character> result = new ArrayList<>();for (char c:s.toCharArray()) result.add(c);return result.stream();}});
System.out.println(Arrays.toString(result.toArray()));

flatMap实际上提供的是“流扁平化操作”,即将多个流合并为一个流的操作。

比如给定两个数字列表,如何返回所有的数对呢?例如,给定列表[1, 2, 3]和列表[3, 4],应该返回[(1, 3), (1, 4), (2, 3), (2, 4), (3, 3), (3, 4)]。

List<Integer> numbers1 = Arrays.asList(1, 2, 3);
List<Integer> numbers2 = Arrays.asList(3, 4);
List<int[]> pairs =numbers1.stream().flatMap(i -> numbers2.stream().map(j -> new int[]{i, j})).collect(toList());
pairs.stream().map(Arrays::toString).forEach(System.out::print);

二、提取子流和组合流

//Stream.limit(n)会返回一个包含n个元素的新流//Stream.skip(n)会丢弃掉前面的n个元素//Stream.concat(Stream s1,Stream s2)拼接两个流//Stream.peek返回相同的流,但是在每次获取一个元素时会执行指定的方法。peek的参数为Function接口
//注意Stream不会按照元素的调用顺序执行,在这个例子中,只有执行到limit方法时,才会回头执行iterate
Object[] powers = Stream.iterate(1.0,p->p*2).peek(e->System.out.println("Fetching "+e)).limit(20).toArray();

三、依赖于被操作流的转换

//distinct()方法,筛选去除重复元素
Stream<String> stringStream = Stream.of("A", "A", "A", "A", "B").distinct();//sorted()方法,在产生任何元素之前进行排序,参数为Compartor接口
Stream<String> words = Stream.of("adfa","adfa","zxcv","qewr","erte");
Stream<String> stringStream2 = words.sorted(String::compareTo);

终止操作:

当一个流使用了终止操作后,就不能再应用其他方法了。

一、简单的聚合方法

所谓“聚合”和数据库SQL中的概念相同,即把多个值“聚合”成一个值

注意“聚合”都是终止操作,在使用聚合方法之后,Stream对象会被关闭不能再进行其他操作

  • min、max方法都接收一个Compartor接口实现,并返回一个Optional<T>值,它可能封装返回值,也可能表示没有返回值(当流为空时)。
Stream<String> words = Stream.of("adfa","adfa","zxcv","qewr","erte");
//聚合求最大值,min方法类似,参数是一个Comparator实例
Optional<String> largest = words.max(String::compareToIgnoreCase);
//Optional对象调用get()方法,如果值不存在不会报null pointer,但是会报NoSuchElement
if (largest.isPresent()){System.out.println("largest:"+largest.get());
}
  • findFirst方法返回非空集合中的第一个值(Optional<T>),通常和filter配合使用:
words = Stream.of("adfa","adfa","zxcv","qewr","erte");
Optional<String> firstElementStartsWithA = words.findFirst().filter(s->s.startsWith("a"));
if (firstElementStartsWithA.isPresent()){System.out.println(firstElementStartsWithA.get());
}
  • findAny方法,在并行计算中返回找到的第一个(并不一定是“排序上的第一个”),通常和filter配合使用:
Stream<String> words = Stream.of("adfa","adfb","zxcv","qewr","erte");
//findFirst一定返回adfa,而findAny可能返回adfa也可能返回adfb
Optional<String> startsWithA =words.parallel().filter(s->s.startsWith("a")).findFirst();System.out.println(startsWithA.get());
}
  • anyMatch方法,判断是否存在符合项,接收一个Predicate对象,返回boolean。allMatch和noneMatch类似,分别在所有元素都符合和没有元素符合时返回true。
Stream<String> words = Stream.of("adfa", "adfb", "zxcv", "qewr", "erte");
boolean hasWordStartWithA = words.parallel().anyMatch(s -> s.startsWith("a"));
System.out.println(hasWordStartWithA);
  • reduce方法接收一个BiFunction二元函数对象,依次处理每两个元素
Stream<Integer> values = Stream.of(1,2,3);
//这里会返回6
Optional<Integer> sum = values.reduce((x,y)->x+y);//可以设定一个对结果没有影响的标志值,这样可以直接返回T类型二无需对Optional进行处理
Integer sum = values.reduce(0,Integer::sum);Stream<String> words = Stream.of("adfasdf","adsfa","ghjk","yioyui");
//如果出现处理的不是T,而不是T的一个属性这种情况,需要额外声明一个"累加器"函数
int result = words.reduce(0,(total,word)->total+word.length(),(total1,total2)->total1+total2);//reduce也可以用来求最大值和最小值
Optional<Integer> maxValue = values.reduce(Integer::max);
Optional<Integer> minValue = values.reduce(Integer::min);

map和reduce组合通常被称为map-reduce模式,这种模式极易实现并行化。

二、收集输出结果

注意下面提到的收集结果的方法同样会终止Stream

  • toArray()方法
Stream<String> words = Stream.of("adfasdf","adsfa","ghjk","yioyui");
//toArray方法,传入需要输出的数组类型,得到数组
String[] strings = words.toArray(String[]::new);
  • iterator()方法
//返回一个Iterator<T>对象
Iterator<String> iterator = words.iterator();
while (iterator.hasNext()){String str = iterator.next();
}
  • collect()方法,把结果输出到set或者list,或者
//输出到List
List<String> list = words.collect(Collectors.toList());
Set<String> set = words.collect(Collectors.toSet());
//也可以指定输出的list、set的类型
TreeSet<String> treeSet = words.collect(Collectors.toCollection(TreeSet::new));//对于字符串,还可以直接把字符串串联起来
String str = words.collect(Collectors.joining());
String str = words.collect(Collectors.joining(","));

Collectors还为“汇总”(求和、求平均等)提供了一系列的操作,例如:

1、counting方法,获取元素个数,maxBy获取最大值,minBy获取最小值

2、summing(Int|Double|Long)方法,对指定的对象的属性进行求和

3、averageing(Int|Double|Long)方法,对制定的对象的属性进行求平均

4、Collectors还有summerizing(Int|Double|Long)方法,可以一次性得到上述的所有统计结果,然后通过getXX()方法获取值

Stream<Integer> numbers = Stream.of(1,2,3,4);
IntSummaryStatistics collect = numbers.collect(Collectors.summarizingInt(Integer::intValue));
long sum = collect.getSum();
double average = collect.getAverage();
int max = collect.getMax();
long count = collect.getCount();
  • 使用collect(Collectors.toMap())把结果转化成map
Person person1 = new Person(1,"cai");
Person person2 = new Person(2,"li");
Person person3 = new Person(3,"wang");
Stream<Person> personStream = Stream.of(person1,person2,person3);
Map<Integer,Person> idToPerson =//toMap()方法接收两个Function对象personStream.collect(Collectors.toMap(Person::getId,p->p));//如果出现由重复Key的情况,多传入一个BiFunction对象
Stream<Locale> locales = Stream.of(Locale.getAvailableLocales());
Map<String,String> languageNames = locales.collect(Collectors.toMap(l->l.getDisplayLanguage(),l->l.getDisplayLanguage(l),//设置当出现两个相同的Key的时候只保留第一个(l1,l2)->l1)
);

toMap()方法要求不能有重复的Key,否则就需要额外定义一个函数进行处理。对“分组”操作非常不便。Collectors的groupingBy、partitionBy方法解决了这个问题:

Person person1 = new Person(1,"cai",true);
Person person2 = new Person(1,"li",true);
Person person3 = new Person(2,"wang",false);
Stream<Person> personStream = Stream.of(person1,person2,person3);
//接收一个Function,为分类函数
Map<Integer, List<Person>> collect = personStream.collect(Collectors.groupingBy(Person::getClassId));
//如果分类函数的返回值是boolean(即一个Predicate对象),则用partitionBy效率更高
personStream.collect(Collectors.partitioningBy(Person::isMale));

groupingBy()方法还可以嵌套一个groupiongBy()进行二级分组。

Map<Dish.Type, Map<CaloricLevel, List<Dish>>> dishesByTypeCaloricLevel =menu.stream().collect(groupingBy(Dish::getType,groupingBy(dish -> {if (dish.getCalories() <= 400) return CaloricLevel.DIET;else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;else return CaloricLevel.FAT;} )));

groupingBy()方法可以嵌套其他归约函数,作为Map的key的值。实际上groupingBy()本身就是groupingBy(f,toList())的简便写法。

//将最大的calory作为值存入
//collectingAndThen方法用于转换返回值的类型
Map<Dish.Type, Dish> mostCaloricByType =menu.stream().collect(groupingBy(Dish::getType,collectingAndThen(maxBy(comparingInt(Dish::getCalories)),Optional::get)));//和summingInt方法联合使用
Map<Dish.Type, Integer> totalCaloriesByType =menu.stream().collect(groupingBy(Dish::getType,summingInt(Dish::getCalories)));//使用toSet()收集
Map<Dish.Type, Set<CaloricLevel>> caloricLevelsByType =menu.stream().collect(groupingBy(Dish::getType, mapping(dish -> { if (dish.getCalories() <= 400) return CaloricLevel.DIET;else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;else return CaloricLevel.FAT; },toSet() )));

partitionBy方法也有类似的用法。

三、返回void的终止方法

forEach是一个返回void的终端操作,它会对源中每一个元素应用一个Lambda。例如:

List.stream().forEach(System.out::println);

转载于:https://my.oschina.net/pierrecai/blog/853509

Java Stream API(一)相关推荐

  1. Java Stream API性能测试

    Java Stream API性能测试 已经对Stream API的用法鼓吹够多了,用起简洁直观,但性能到底怎么样呢?会不会有很高的性能损失?本节我们对Stream API的性能一探究竟. 为保证测试 ...

  2. Java Stream API入门篇

    转自:https://www.cnblogs.com/CarpenterLee/p/6545321.html 你可能没意识到Java对函数式编程的重视程度,看看Java 8加入函数式编程扩充多少功能就 ...

  3. go语言如何调用java接口_Go语言实现的Java Stream API

    学习Go语言时实现的集合操作工具库,类似于Java 8 中新增的Stream API.由于Go语言不支持泛型,所以基于反射实现.只用于学习目的,不要用于生产(PS:当然也不会有人用). 集合操作包括生 ...

  4. java stream api 对象复制_Java 8新特性之旅:使用Stream API处理集合

    在这篇"Java 8新特性教程"系列文章中,我们会深入解释,并通过代码来展示,如何通过流来遍历集合,如何从集合和数组来创建流,以及怎么聚合流的值. 在之前的文章"遍历.过 ...

  5. stream去重_使用Java Stream API中DistinctBy删除重复数据

    Stream API提供distinct()方法,该方法基于数据Object类的equals()方法返回列表的不同元素.下面先做一个数据Object类,用来发现重复数据: public class L ...

  6. 使用Java Stream API将List按自定义分组规则转换成Map的一个例子

    本文完整测试代码见文末. 测试数据是List里的4个员工对象实例: 根据员工所在的城市进行分组: 结果分成了三组: 第一组的员工在上海: 第二组的员工在成都: 统计每组员工个数: 把员工进行分组,得分 ...

  7. java list 自定义类型转换_使用Java Stream API将List按自定义分组规则转换成Map的一个例子...

    本文完整测试代码见文末. 测试数据是List里的4个员工对象实例: 根据员工所在的城市进行分组: 结果分成了三组: 第一组的员工在上海: 第二组的员工在成都: 统计每组员工个数: 把员工进行分组,得分 ...

  8. Java Stream API进阶篇

    转自:https://www.cnblogs.com/CarpenterLee/p/6550212.html 上一节介绍了部分Stream常见接口方法,理解起来并不困难,但Stream的用法不止于此, ...

  9. Java 8 Stream Api 中的 skip 和 limit 操作

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 1. 前言 Java 8 Stream API 中的sk ...

最新文章

  1. [征询意见]北京.NET俱乐部11月份活动内容
  2. jquery ajax 与 flask 传输 json 并且 提取ajax数据作为全局变量
  3. php cookie删除不了,php cookie删除不了怎么办
  4. 计算机应用 能力考试的意义,关于大学生计算机应用创新能力培养的分析
  5. C语言 socket 编程学习
  6. AJAX跨域请求的理解,JAVA
  7. ctfshow-WEB-web10( with rollup注入绕过)
  8. 中国移动MM,你需要了解
  9. xp系统关于无线网络连接服务器,八步骤搞定XP系统无线网络设置问题
  10. cad解除块的快捷命令_Auto CAD2007解除块快捷键是什么呢?
  11. 联想y450安装黑苹果
  12. 【不看即后悔系列】学习的真相及方法【建议收藏】
  13. WordSequence API
  14. Image Processing and Analysis_8_Edge Detection:Finding Edges and Lines in Images by Canny——1983...
  15. PHP下载APK文件
  16. 怎么制作画中画视频?学会这几招轻松实现
  17. KBPC5010-ASEMI大功率整流桥、50A整流桥
  18. 计算机一级wpsoffice知识点,2016年计算机一级《WPS Office》考试大纲
  19. 资源共享(好东西,要分享)
  20. 软件测试最全面试题,了解一下

热门文章

  1. 那些理工生与古诗佬李白等的对饮“诗光”
  2. MATLAB矩阵乘法、MATLAB矩阵的转置、MATLAB串联矩阵
  3. 程序员讨论《黑客帝国》(二)平衡和进化
  4. NewStarCTF 公开赛赛道
  5. Collapsing margins(外边距合并)
  6. cordova-plugin-alipay-v2使用沙箱环境测试支付
  7. 3dcnn视频分类算法-pytorch上分之路
  8. 我来说说 Vue面试知识点 和 Vue3.0
  9. java容器之七_TreeMap与红黑二叉树
  10. 深入JVM读书笔记之类装载+连接+初始化