JAVA8--Stream学习

简介: Stream是什么怎么使用StreamStream的建立Stream中的元素操作Stream聚合操作Stream结果处理Stream分组操作Stream注意事项Stream是什么书上说Stream是对JAVA中对集合处理的抽象,在我看来Stream更像是对java集合的一次扩展,因为Stream中的API都是我们对集合操作中可能遇

Stream是什么

书上说Stream是对JAVA中对集合处理的抽象,在我看来Stream更像是对java集合的一次扩展,因为Stream中的API都是我们对集合操作中可能遇到的问题。那为什么要用Stream呢?可以从两个方面去考虑,一方面,使得集合处理可以更加高效,Stream可以并行执行。另一方面,代码更佳优雅,更加简短,因为Stream中也用到了函数式编程接口。

举一个例子来看使用Stream的优势。对一个String集合中的所有字符串做统计,找出长度大于5的字符串。

//集合内容
List<String> list = new ArrayList<>();
list.add("a");
list.add("bc");
list.add("def");
list.add("ghij");
list.add("kmlno");
list.add("pqrstu");

使用传统的集合操作

int count =0;
for(String s : list){
    if (s.length()>5){
        count++;
    }
}

使用Stream API进行处理

//串行执行
long count = list.stream().filter(s->s.length()>5).count()
//并行执行
long count = list.parallelStream().filter(s->s.length()>5).count()

相比于传统的迭代的模式,使用Stream对集合进行操作会更加简洁、方便、高效。

怎么使用Stream

使用Stream只需要三个步骤:

  • 创建一个Stream
  • 指定这个Stream要做的操作
  • 使用一个终止动作来使得Stream产生结果

上面的字符串长度统计的例子中,list.stream()用来建立了一个Stream,filter(s->s.length()>5)告诉了这个流要做哪些事情,最后使用count操作来强制它之前的延迟操作立即执行。Stream执行的一个特点就是延迟执行,count方法被调用的时候才会执行Stream之前定义的操作。另外,Stream执行的过程中不会对源对象产生改变,它们会返回一个新的Stream。

Stream的建立

JAVA8在Collection接口中添加了stream方法,所以我们可以将任何集合转换为一个Stream。另外我们也可以通过Stream.of方法将一个数组转换为Stream。

//集合到Stream
Stream<String> stream = list.stream();
//数组到Stream
String contents = "a,b,c,d,e";
Stream<String> stream = Stream.of(contents.split(","));
//构造含有任意个数的Stream,Stream.of方法接受可变长度的参数
Stream<String> stream = Stream.of("q","w","e","r");

另外还可以建立无限序列,如建立一个行如0 1 2 3 4 5 …的无限序列

Stream<BigInteger> stream = Stream.iterate(BigInteger.ZERO,n->n.add(BigInteger.ONE));

另外一种创建无限Stream的方法是generate方法,当需要一个Stream值的时候就会调用该方法来产生一个包含随机数的无限流。

Stream<Double> stream = Stream.generate(Math::random);

Stream中的元素操作

Stream的过滤操作可以使用filter方法,该方法会返回一个新的流,这个流中的元素满足一定的条件。接口方法的定义如下所示

Stream<T> filter(Predicate<? super T> predicate);

该方法的参数是一个Predicate<T>对象,是一个从T->boolean的函数。

如果需要对一个流中的每个元素都做一定的转换操作而不是进行条件过滤,如将一个字符串流中每个元素变为小写,那么可以使用map方法。

Stream<String>  stream = list.stream().map(String::toLowerCase);    

在使用迭代方式对集合进行处理的过程中,我们可以通过控制台打印的方式查看元素的执行情况。

for(集合){
...
打印元素,观察状态
...
}

在使用Stream流的过程中,可以使用peek方法完成相同的事情。该方法会产生一个与原始流具有相同元素的流,但是在每次获取一个元素时,都会调用一个函数,这样利于调试。

String[]  stream =list.stream().peek(e->System.out.println("Fetching:"+e)).map(String::toLowerCase).toArray(String[]::new);

执行结果:

Fetching:a
Fetching:bc
Fetching:def
Fetching:ghij
Fetching:kmlno
Fetching:pqrstu

如果只是简单的打印流里面的元素,可以使用

list.stream().forEach(System.out::println)

Stream聚合操作

前面说过流的执行过程是延迟执行,当一个流遇到了终止操作后,它前面的操作才会执行。聚合方法都是终止操作。它会将流聚合为一个值。常用的聚合方法有返回流中的元素总数count方法。返回流中的最大值和最小值的max和min方法。findFirst方法返回非空集合中的第一个值。findAny方法返回所有匹配的元素。anyMatch方法返回流中是否有匹配的元素。
例子:

Optional<String> optional =  list.stream().max(String::compareToIgnoreCase);
if (optional.isPresent()){
System.out.println(optional.get());   
}

在执行这些方法时,返回值有可能为空,如果对返回值不加以判断就直接执行之后的代码,很有可能会引起空指针异常。JAVA8中推出了一个Optional<T>类型,这是一种更好表示缺少返回值的方式。它是对一个T类型对象的封装或者表示不是任何对象,它比一般的指向T类型的引用更加安全。但是从上面的例子可以看出,使用Optional跟以前的直接对返回值进行判断并没有什么简化。如果没有设计Optional,那么上面的代码调用可能是下面的方式。

T value =  list.stream().max(String::compareToIgnoreCase);
if (value != null){
System.out.println(value);
}

所以高效使用Optional的关键是把它当作一个中间结果,我们可以把这个中间结果传递给其它函数做处理。基本形式:
聚合值->Optional->函数处理,如我们要把上面比较的结果添加到一个result集合中去,那么可以使用

optional.ifPresent(value -> result.add(value));

有关Optional的详细信息可以参考这篇博文: Java 8 Optional类深度解析

Stream结果处理

在流处理的过程中,通常是要把处理的结果转换为集合对象,而不是要聚合为一个值。
流->数组

String[] result = list.stream().toArray(String[]::new)

流->set

Set<String> result = list.stream().collect(Collectors.toSet());

流->list

List<String> result = list.stream().collect(Collectors.toList());

流->map
由于map是k-v形式,所以要指明KV。如我们有一个Stream<Person>对象,将它的id和name存入map中。

Map<Integer,String> idToName = stream.collect(Collectors.toMap(Person::getId,Person::getName))

如果key为id,值为person对象可以用以下方法获取map,如果key重复,会抛出异常。

Map<Integer, Person> idToPerson = stream.collect(Collectors.toMap(Person::getId, Function.identity()));

Stream分组操作

分组操作实际上是对map操作的一种简化与补充。如我们现在需要得到一个map,key为国家名,value为改国家所使用的语言。可以用以下代码实现:

Stream<Locale> locales = Stream.of(Locale.getAvailableLocales());

Map<String, Set<String>> countryLanguageSets = locales.collect(
Collectors.toMap(Locale::getDisplayCountry,
l -> Collections.singleton(l.getDisplayLanguage()),
(a, b) -> {
Set<String> r = new HashSet<>(a); 
r.addAll(b);
return r; }
));

与上面生成map的方法相比,该方法多了一个参数,第三个参数的作用是在发现了一个国家的一种新语言时,我们将已有值和新值组成一个新的集合。上面实际上是对locale流按照国家来分组。所以也可以用以下代码实现

Map<String, List<Locale>> countryToLocales = locales.collect(
Collectors.groupingBy(Locale::getCountry));

Locale::getCountry为分组的依据。如果我们需要对分组后的结果在做一些处理,如转换为set或统计数量

locales = Stream.of(Locale.getAvailableLocales());
Map<String, Set<Locale>> countryToLocaleSet = locales.collect(
groupingBy(Locale::getCountry, Collectors.toSet()));
System.out.println("countryToLocaleSet: " + countryToLocaleSet);   

locales = Stream.of(Locale.getAvailableLocales());
Map<String, Long> countryToLocaleCounts = locales.collect(
groupingBy(Locale::getCountry, counting()));
System.out.println("countryToLocaleCounts: " + countryToLocaleCounts);  

Stream注意事项

当执行一个流操作时,并不会对底层的集合进行修改。

目录
相关文章
|
26天前
|
Java API Maven
2025 Java 零基础到实战最新技术实操全攻略与学习指南
本教程涵盖Java从零基础到实战的全流程,基于2025年最新技术栈,包括JDK 21、IntelliJ IDEA 2025.1、Spring Boot 3.x、Maven 4及Docker容器化部署,帮助开发者快速掌握现代Java开发技能。
263 1
|
1月前
|
数据采集 搜索推荐 Java
Java 大视界 -- Java 大数据在智能教育虚拟学习环境构建与用户体验优化中的应用(221)
本文探讨 Java 大数据在智能教育虚拟学习环境中的应用,涵盖多源数据采集、个性化推荐、实时互动优化等核心技术,结合实际案例分析其在提升学习体验与教学质量中的成效,并展望未来发展方向与技术挑战。
|
22天前
|
存储 Java API
Java Stream API:现代数据处理之道
Java Stream API:现代数据处理之道
201 92
|
22天前
|
存储 Java API
Java Stream API:现代数据处理之道
Java Stream API:现代数据处理之道
142 68
|
2月前
|
并行计算 Java API
Java 基础篇完整学习攻略
本教程涵盖Java基础到高级内容,包括模块化系统、Stream API、多线程编程、JVM机制、集合框架及新特性如Records和模式匹配等,适合零基础学员系统学习Java编程。
71 0
|
2月前
|
Oracle Java 关系型数据库
掌握Java Stream API:高效集合处理的利器
掌握Java Stream API:高效集合处理的利器
329 80
|
2月前
|
安全 Java API
Java 8 Stream API:高效集合处理的利器
Java 8 Stream API:高效集合处理的利器
221 83
|
2月前
|
前端开发 Java API
新手 Java 学习资料结合最新技术的精选推荐及高效学习资源参考
本文为新手推荐了涵盖Java基础到最新技术的学习资料,包括官方文档、在线课程、书籍、学习网站及实践平台,帮助系统掌握Java编程,并通过Spring Boot实战提升开发能力。
114 1
|
2月前
|
NoSQL Java 数据库
Java 全栈学习超全面知识图谱构建完整 Java 知识体系
本文全面讲解Java核心技术体系,涵盖基础语法、面向对象、集合框架、主流框架(Spring、Spring Boot、MyBatis)及三大实战项目(微服务电商、响应式博客、企业后台系统),助你系统掌握Java全栈开发技能。
178 1
|
2月前
|
存储 算法 安全
JAVA 八股文全网最详尽整理包含各类核心考点助你高效学习 jAVA 八股文赶紧收藏
本文整理了Java核心技术内容,涵盖Java基础、多线程、JVM、集合框架等八股文知识点,包含面向对象特性、线程创建与通信、运行时数据区、垃圾回收算法及常用集合类对比,附有代码示例与学习资料下载链接,适合Java开发者系统学习与面试准备。
678 0

热门文章

最新文章