Java8中的Stream流式操作教程之王者归来

网友投稿 352 2022-12-28


Java8中的Stream流式操作教程之王者归来

前言

相对于java8之前的Java的相关操作简直是天差地别,Java8 的流式操作的出现,也很大程度上改变了开发者对于Java的繁琐的操作的印象,从此,Java也走向了函数式编程的道路!

1 流的创建

1.1 流的创建方法

既然需要聊聊流的操作,那么,首先还是先看看怎么创建流。

创建流的方法有三种,分别是:Stream.of()、Stream.iterate()、Stream.generate(),然后,分别看一下这三个方法的声明。

static Stream of(T... values)

static Stream iterate(T seed, UnaryOperator f)

static Stream generate(Supplier s)

Stream.of():参数很简单,就是一系列的泛型参数。Stream.iterate():第一个参数是一个初始值,第二个参数是一个操作。

Stream.generate():参数就是一个Supplier的供给型的参数。

1.2 流的创建方法举例

/*

* @Author 欧阳思海

* @Description 创建流

* @Date 11:05 2019/8/26

* @Param []

* @return void

**/

@Test

public void testCreateStream() {

//利用Stream.of方法创建流

Stream stream = Stream.of("hello", "world", "Java8");

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

System.out.println("##################");

//利用Stream.iterate方法创建流

Stream.iterate(10, n -> n + 1)

.limit(5)

.collect(Collectors.toList())

.forEach(System.out::println);

System.out.println("##################");

//利用Stream.generate方法创建流

Stream.generate(Math::random)

.limit(5)

.forEach(System.out::println);

System.out.println("##################");

//从现有的集合中创建流

List strings = Arrays.asList("hello", "world", "Java8");

String string = strings.stream().collect(Collectors.joining(","));

System.out.println(string);

}

在上面的例子中,Stream.of()方法的参数是几个字符串,Stream.iterate()方法的第一个参数是初始值 10,第二个参数是在10 的基础上每次加 1 的操作,Stream.generate()的参数是用 Random 方法产生随机数。

1.3 流的创建总结

流的创建有三种方法,分别是Stream.of()、Stream.iterate()、Stream.generate(),这几个都是 Stream 类的静态方法,所以,使用起来非常的方便。

2 流的操作

在上一节中,我们知道怎么创建流了,接下来,我们就看看对流可以进行哪些操作,使用了 Stream 流之后,是否会比 Java8 之前方便很多呢?

2.1 装箱流

在处理对象流的时候,可以利用 Collectors 类的静态方法转换为集合,例如,将字符串流转换为  List ,这种方式是没有问题的。

但是,如果遇到 double流想要转换为 List 时,这是就会报错。

DoubleStream.of(1.0, 2.0, 3.0)

.collect(Collectors.toList());//错误的写法

这种方式就是错误的,编译是不能通过的。

别慌,对于这种问题,有 3 种比较好的解决方法。

利用 boxed 方法

利用 boxed 方法,可以将 DoubleStream 转换为 Stream ,例如;

DoubleStream.of(1.0, 2.0, 3.0)

.boxed()

.collect(Collectors.toList());

这样就解决了上面的问题。

利用 mapToObj 方法

利用 mapToObj 方法也可以实现上面的功能,另外,也提供了 mapToInt、mapToLong、mapToDouble 等方法将基本类型流转换为相关包装类型。

DoubleStream.of(1.0, 2.0, 3.0)

.boxed()

.collect(Collectors.toList());

collect 方法

一般情况下,我们利用 collect 方法的时候,都是用于将流的数据收集为基本类型的集合,例如;

stream.collect(Collectors.toList())

然而,collect 方法其实还有一种更加一般化的形式,如下;

R collect(Supplier supplier,

ObjIntConsumer accumulator,

BiCnsumer combiner)

上面这种方法的第一个参数是一个供给器,相当于初始化一个容器,第二个参数是累加器,相当于给初始化的容器赋值,第三个参数是组合器,相当于将这些元素全部组合到一个容器。

下面,我们通过一个简单的例子来看看到底是怎么使用的!

List list = DoubleStream.of(1.0, 2.0, 3.0)

.collect(ArrayList::new, ArrayList::add, ArrayList::addAll);

上面的例子我们可以看到,第一个参数:使用一个静态方法初始化一个 List 容器,第二个参数:使用静态方法 add ,添加元素,第三个参数:使用静态方法 addAll ,用于联合所有的元素。

从最后的返回值为 List,我们也可以看出,全部组合成一个初始化的 List 集合中了。

2.2 字符串与流之间的转换

这一小节主要讲解一下字符串与流之间的转换,将 String 转为流有两种方法,分别是 java.lang.CharSequence 接口定义的默认方法 chars 和 codePoints ,而将流转为字符串就是我们前面已经讲解到的方法 collect 。

/*

* @Author 欧阳思海

* @Description 字符串与流之间的转换

* @Date 9:41 2019/9/2

* @Param []

* @return void

**/

@Test

public void testString2Stream() {

String s = "hello world Java8".codePoints()//转换成流

.collect(StringBuffhttp://er::new,

StringBuffer::appendCodePoint,

StringBuffer::append)//将流转换为字符串

.toString();

String s1 = "hello world Java8".chars()//转换成流

.collect(StringBuffer::new,

StringBuffer::appendCodePoint,

StringBuffer::append)//将流转换为字符串

.toString();

}

在上面的例子中,先用chars 和 codePoints 方法转换为流,然后都是利用 collect 方法再转回字符串。

2.3 流的映射 map 与 flatMap

流的映射是什么意思呢,我们先将一个在 Java8 之前的例子,我们常常需要将一个集合的对象的某一个字段取出来,然后再存到另外一个集合中,这种场景我们在 Java8 之前我们会这样实现。

/*

* @Author 欧阳思海

* @Description Java8之前的用法

* @Date 19:31 2019/9/2

* @Param []

* @return void

**/

@Test

public void testList() {

List list = new ArrayList<>();

List friends = new ArrayList<>();

friends.add(new Friend("Java5"));

friends.add(new Friend("Java6"));

friends.add(new Friend("Java7"));

Person person = new Person();

person.setFriends(friends);

list.add(person);

List strings = new ArrayList<>();

for(Person p : list){

strings.add(p.getName());

}

}

是不是这样很麻烦,这也就是以前大家一直所说的 python 用一招,Java 需要用花招!

但是,Java8 却改变了这种现实,我们来看一看怎么使用 map 和 flatMap。

首先,我们先看一下这俩个方法的声明;

Stream map(Function super T,? extends R> mapper)

Stream flatMap(Function super T,? extends Stream extends R>> mapper)

接下来,我们用这两个方法改写上面的方式,先看看 map 方法;

/*

* @Author 欧阳思海

* @Description map、flatMap方法

* @Date 9:50 2019/9/2

* @Param []

* @return void

**/

@Test

public void testMapAndFlatMap() {

List list = new ArrayList<>();

List friends = new ArrayList<>();

friends.add(new Friend("Java5"));

friends.add(new Friend("Java6"));

friends.add(new Friend("Java7"));

Person person = new Person();

person.setFriends(friends);

list.add(person);

//映射出名字

List strings = list.stream().map(Person::getName).collect(Collectors.toList());

}

通过使用 map 方法,参数给定 Person::getName 映射出 name,然后再用 collect 收集到 List 中,就完成了上面的负责的操作,是不是很舒服。

但是,如果我们用 map 方法想要映射出 friends 属性,会遇到一个问题;

//映射出朋友

List> collect = list.stream().map(Person::getFriends).collect(Collectors.toList());

我们发现,上面的返回值是 List>,这种形式集合里面还包着集合,处理有点麻烦,但是,不是还有另外 flatMap 没有使用吗,这个方法正好能够解决这个问题。

List collect1 dMMXO= list.stream().flatMap(friend -> friend.getFriends().stream()).collect(Collectors.toList());

发现,这个方法的返回值是 List,正如我们看到的,flatMap 的方法能够“展平”包裹的流,这就是 map 和 flatMap 的区别。

2.4 流的连接

流的连接有两种方式,如果是两个流的连接,使用 Stream.concat 方法,如果是三个及三个以上的流的连接,就使用 Stream.flatMap 方法。

/**

* @return void

* @Author 欧阳思海

* @Description 流的连接

* @Date 10:13 2019/9/2

* @Param []

**/

@Test

public void testConcatStream() {

//两个流的连接

Stream first = Stream.of("sihai", "sihai2", "sihai3");

Stream second = Stream.of("sihai4", "sihai5", "sihai6");

Stream third = Stream.of("siha7", "sihai8", "sihai9");

Stream concat = Stream.concat(first, second);

//多个流的连接

Stream stringStream = Stream.of(first, second, third).flatMap(Function.identity());

}

3 流的规约操作

流的规约操作几种类型,这里都讲一下。

内置的规约操作

基本类型流都有内置的规约操作。包括average、count、max、min、sum、summaryStatistics,前面的几个方法相信不用说了,summaryStatistics 方法是前面的几个方法的结合,下面我们看看他们如何使用。

/**

* @return void

* @Author 欧阳思海

* @Description 内置规约操作

* @Date 22:04 2019/9/1

* @Param []

**/

@Test

public void testReduce1() {

String[] strings = {"hello", "sihai", "hello", "Java8"};

long count = Arrays.stream(strings)

.map(String::length)

.count();

System.out.println(count);

System.out.println("##################");

int sum = Arrays.stream(strings)

.mapToInt(String::length)

.sum();

System.out.println(sum);

System.out.println("##################");

OptionalDouble average = Arrays.stream(strings)

.mapToInt(String::length)

.average();

System.out.println(average);

System.out.println("##################");

OptionalInt max = Arrays.stream(strings)

.mapToInt(String::length)

.max();

System.out.println(max);

System.out.println("##################");

OptionalInt min = Arrays.stream(strings)

.mapToInt(String::length)

.min();

System.out.println(min);

DoubleSummaryStatistics statistics = DoubleStream.generate(Math::random)

.limit(1000)

.summaryStatistics();

System.out.println(statistics);

}

就是这么简单!

基本的规约操作

基本的规约操作是利用前面讲过的 reduce 方法实现的,IntStream 接口定义了三种 reduce 方法的重载形式,如下;

OptionalInt reduce(IntBinaryOperator op)

int reduce(int identity, IntBianryOperator op)

U reduce(U identity,

BiFunction accumulator,

BianryOperator combiner)

上面的 identity 参数就是初始化值的意思,IntBianryOperator 类型的参数就是操作,例如 lambda 表达式;

BianryOperator combiner是一个组合器,在前面有讲过。

下面我们通过一个例子来讲解一下。

/**

* @return void

* @Author 欧阳思海

* @Description reduce规约操作

* @Date 22:20 2019/9/1

* @Param []

**/

@Test

public void testReduce2() {

int sum = IntStream.range(1, 20)

.reduce((x, y) -> x + y)

.orElse(0);

System.out.println(sum);

System.out.println("##################");

int sum2 = IntStream.range(1, 20)

.reduce(0, (x, y) -> x + 2 * y);

System.out.println(sum2);

System.out.println("##################");

int sum3 = IntStream.range(1, 20)

.reduce(0, Integer::sum);

System.out.println(sum3);

}

例子中的第一个是1到20累加的操作,第二个以0为初始值,然后2倍累加,第三个是以0为初始值,累加。

流的计数

流的数量统计有两种方法,分别是 Stream.count() 方法和 Collectors.counting() 方法。

/**

* @return void

* @Author 欧阳思海

* @Description 统计测试

* @Date 23:29 2019/9/1

* @Param []

**/

@Test

public void testStatistics() {

//统计数量

String[] strings = {"hello", "sihai", "hello", "Java8"};

long count = Arrays.stream(strings)

.count();

System.out.println(count);

System.out.println("##################");

Long count2 = Arrays.stream(strings)

.collect(Collectors.counting());

System.out.println(count2);

}

4 流的查找与匹配

流的查找

流的查找 Stream 接口提供了两个方法 findFirst 和 findAny。

findFirst 方法返回流中的第一个元素的 Optional,而 findAny 方法返回流中的某个元素的 Optional。

我们来看一个例子。

String[] strings = {"hello", "sihai", "hello", "Java8"};

Optional first = Arrays.stream(strings)

.findFirst();

System.out.println(first.get());

System.out.println("##################");

Optional any = Arrays.stream(strings).findAny();

System.out.println(any.get());

System.out.println("##################");

流的匹配

流的匹配 Stream 接口提供了三个方法,分别是 anyMatch(任何一个元素匹配,返回 true)、allMatch(所有元素匹配,返回 true)、noneMatch(没有一个元素匹配,返回 true)。

boolean b = Stream.of(1, 2, 3, 4, 5, 10)

.anyMatch(x -> x > 5);

System.out.println(b);

System.out.println("##################");

boolean b2 = Stream.of(1, 2, 3, 4, 5, 10)

.allMatch(x -> x > 5);

System.out.println(b2);

System.out.println("##################");

boolean b3 = Stream.of(1, 2, 3, 4, 5, 10)

.noneMatch(x -> x > 5);

System.out.println(b3);

5 流的总结

这篇文章主要讲解了流的一些操作,包括下面几个方面。

- 流的创建方法。

- 流的系列操作,包括装箱流、字符串与流之间的转换、流和映射 map 和 flatMap、流的连接。

- 流的规约操作

- 流的查找与匹配

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对我们的支持。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:spring设置拦截器代码实例
下一篇:微信小程序 接口测试工具(微信小程序接口自动化测试)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~