Java8新特性详解(二)Stream API使用
创始人
2025-05-28 20:57:49
0

Stream API 有什么作用?

可以像SQL语句一样对集合、数组进行操作。

特点:

① Stream不会存储元素

② Stream 不会改变原对象

③ Stream 的 中间操作方法 要等到 终止操作方法 被调用时才会执行

一,Stream的创建方式

public class TestStream {public static void main(String[] args) {//1,通过集合的stream方法Stream stream1 = new ArrayList().stream();Stream stream2 = new HashSet().stream();//2.通过Arrays的stream方法Stream stream3 = Arrays.stream(new Integer[]{1, 2, 3});//3.通过Stream类本身提供的方法//3.1 of方法Stream stream4 = Stream.of("aaa", "bbb");//3.2 iterate方法,会像死循环一样不停的运行,所以用了limit(10)限制其只迭代10次//第一个参数是初始值,第二个参数是Function接口的子接口Stream stream5 = Stream.iterate(0, x -> x + 2);stream5.limit(10).forEach(System.out::println);//等同于下面的代码int i=0;int x = 0;while (true){if(i>=10){break;}x = x+2;System.out.println(x);i++;}//3.3 generate方法,参数是一个Supplier接口,会像死循环一样不停的运行,所以用了limit(10)限制其只迭代10次Stream stream6 = Stream.generate(()->Math.random());stream6.limit(10).forEach(System.out::println);}
}

二,中间操作

多个中间操作方法可以连起来使用,在调用终止操作方法前,中间操作不会执行,而在终止操作时一次性执行完成,这叫"惰性求值"

中间操作的方法包含:

方法

说明

filter(Predicate p)

筛选,匹配符合规则的元素

distinct()

筛选,通过hashcode 和 equals去除重复元素

limit(long maxSize)

筛选,取不超过给定数量的元素

skip(long n)

筛选,跳过前n个元素,若流中元素不足n个则返回空流

map(Function f)

映射,输入一个T类型的元素,返回一个R类型的元素;

如果返回的类型是也是Stream类型,会直接把Stream放入原Stream中

mapToDouble(ToDoubleFunction f)

映射,输入一个T类型的元素,返回一个Double类型的元素

mapToLong(ToLongFunction f)

映射,输入一个T类型的元素,返回一个Long类型的元素

flatMap(Function f)

映射,输入一个T类型的元素,返回一个R类型的元素;

如果返回的类型是也是Stream类型,会把Stream中的元素取出放入原Stream中;

sorted()

排序,按自然顺序排序,产生一个新流

sorted(Comparator comp)

排序,按比较器排序,产生一个新流

如果只做中间操作,是不会产生任何执行的,比如:

//定义User类
public class User {private String name;private int age;public User(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {System.out.println("getAge 方法执行");return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +'}';}
}//测试类
public class Test1 {static List userList = Arrays.asList(new User("A",20),new User("B",25),new User("C",30));public static void main(String[] args) {//需求:获取age>20的userStream userStream = userList.stream().filter(user -> user.getAge() > 20);System.out.println("运行结束");}
}

运行后输出如下,并未输出 getAge方法内的内容:

所以中间操作需要与终止操作一起使用,例如使用终止操作forEach方法进行输出。

示例一: filter(Predicate p) 的使用

public static void main(String[] args) {//需求:获取age>20的useruserList.stream().filter(user -> user.getAge() > 20).forEach(System.out::println);System.out.println("运行结束");}

运行结果如下:

示例二: limit 的使用

public static void main(String[] args) {//需求:获取age>20的useruserList.stream().filter(user -> user.getAge() > 20).limit(1).forEach(System.out::println);System.out.println("运行结束");}

输出结果如下:

可以看出一旦达到limit的个数,遍历就会结束,不会继续遍历剩下的元素。

示例三: skip的使用

public static void main(String[] args) {//需求:获取age>20的useruserList.stream().skip(2).forEach(System.out::println);System.out.println("运行结束");}

输出结果如下:

示例四: distinct的使用

由于根据hashcode和equals去重复,所以User类需重写hashcode和equals方法才可去重

//定义User类,重写hashcode和equals方法
public class User {private String name;private int age;public User(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {System.out.println("getAge 方法执行");return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +'}';}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;User user = (User) o;return age == user.age && Objects.equals(name, user.name);}@Overridepublic int hashCode() {return Objects.hash(name, age);}
}//测试类
public class Test1 {//姓名C的User有3个重复的static List userList = Arrays.asList(new User("A",20),new User("B",25),new User("C",30),new User("C",30),new User("C",30));public static void main(String[] args) {//需求:获取age>20的useruserList.stream().distinct().forEach(System.out::println);System.out.println("运行结束");}
}

输出结果如下,只输出了一个姓名为C的用户,说明已经去重复:

示例五: map(Function f) 的使用

public static void main(String[] args) {//需求:将数组中的每个元素转大写//1.使用lambda表达式实现Function接口Arrays.stream(new String[]{"aaa","bbb","ccc"}).map(str->str.toUpperCase()).forEach(System.out::println);System.out.println("-----------------------");//2.使用lambda方法引用实现Function接口Arrays.stream(new String[]{"aaa","bbb","ccc"}).map(String::toUpperCase).forEach(System.out::println);
}

如果不清楚lambad表达式和方法引用的写法,可以看前一篇文章

https://blog.csdn.net/weixin_43833851/article/details/129507783

运行结果如下:

示例六: flatMap(Function f) 的使用

与map的区别:

当map函数的返回值类型是也是Stream类型(Stream类型可以包含多个元素)时,

map会把 新Stream 直接 放入原Stream中,

flatMap会把 新Stream中的元素 一个个取出放入 原Stream中;

就好像是集合的add方法,flatMap就好像是集合的addAll方法,

示例:

public static void main(String[] args) {//需求:将数组中的每个元素拆分成单个字符输出//使用map函数,将toCharStream方法返回的 Stream作为一个元素 直接放到原Stream中Stream> outStream = Arrays.stream(new String[]{"aaa", "bbb"}).map(str -> toCharStream(str));//需要嵌套forEach才能完全遍历outStream.forEach(innerStream->innerStream.forEach(System.out::println));System.out.println("------------------------------");//使用flatmap函数,将toCharStream方法返回的 Stream 里面的元素取出,放入到原 stream中Stream flatStream = Arrays.stream(new String[]{"aaa", "bbb", "ccc"}).flatMap(str -> toCharStream(str));//不用嵌套forEachflatStream.forEach(System.out::println);}//将一个字符串转为 Stream,可以把Stream也看成是一种集合,里面存的是字符串拆成的字符
public static Stream toCharStream(String str){List list = new ArrayList<>();for (Character ch :str.toCharArray()){list.add(ch);}return list.stream();
}

运行结果如下:

如果觉得不好理解,看这个List的例子是否可以理解:

public static void main(String[] args) {ArrayList outlist = new ArrayList();outlist.add("aaa");ArrayList inlist = new ArrayList();inlist.add("bbb");inlist.add("ccc");//使用add方法添加一个集合outlist.add(inlist);System.out.println("add方法添加inlist:"+outlist);System.out.println("------------------");outlist = new ArrayList();outlist.add("aaa");//使用addAll方法添加一个集合outlist.addAll(inlist);System.out.println("addAll方法添加inlist:"+outlist);
}

输出如下:

add方法(好比map方法)将整个inlist作为一个元素放入outlist中,因此存在中括号;

addAll方法(好比flatMap方法)将inlist中的元素取出放入outlist中,因此无中括号。

三,终止操作

方法

说明

allMatch(Predicate p)

流中元素是否全部匹配规则p,全部匹配返回true,否则false

anyMatch(Predicate p)

流中元素是否有一个匹配规则p,有匹配返回true,无则false

noneMatch(Predicate p)

流中元素是否全部不匹配规则p,

findFirst()

取流中第一个元素

findAny()

从流中任取一个元素

forEach(Consumer con)

遍历并逐个消费元素

相关内容

热门资讯

修复 爱普生 EPSON L4... L4151 L4153 L4156 L4158 L4163 L4165 L4166 L4168 L4...
【PdgCntEditor】解... 一、问题背景 大部分的图书对应的PDF,目录中的页码并非PDF中直接索引的页码...
监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
【前端】‘??‘与‘||‘有什... 0 问题 经常写const data = res.data.a ?? ''或者const d...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
牛客计算器的改良(Python... 文章目录1.题目描述2.输入描述:3.输出描述:4.示例15.分析6.代码7.结语 链接࿱...
正大杯|市调大赛|2023备赛... 关键信息 同时随着精细化养宠趋势的深入,宠物消费类目日渐丰富。 本报告通过 Niuco...
QGIS下载在线地图(Goog... 前言 国内外有很多在线地图下载软件,但功能单一,基本上只能下载数据&#x...