最近重新回顾jdk8,除了lambda表达式这种有趣的写法外,其中非常重要的两个特性,一个stream,一个函数式编程,今天先来讲讲stream;
一.引子
之前玩jdk8,stream在打印中,我碰到了一个异常错误,如下:
Exception in thread “main” java.lang.IllegalStateException: stream has already been operated upon or closed
该异常抛出时的原代码是这样的,
Stream stm = Stream.of("test", "123", "3");
stm.forEach(System.out::println);
System.out.println(stm.collect(Collectors.toList()));
如果干掉了第二步,则能正常打印,如下:
[test, 123, 3]
这就有意思了,从代码字面意思上来看,第二步是遍历打印元素,第三步是打印整个list,但是明显第二步影响了第三步的操作,这里面就要好好了解关于stream流的一些原理了。
关于该异常,根据异常信息,顾名思义,就是“流已操作或关闭”,换句话说,就是stream不能被复用,只能使用一次。这也符合流的定义,在servlet中的getInputStream只能获取一次(包括输入输出流)。
流的API设计使用了**管道(pipelines)**模式,对流的一次操作会返回另一个流,如同IO的API或者StringBuffer的append方法那样,从而可以使多个不同的操作在一个语句里串起来。
所谓的流,就是江河湖海,你在这一刻看到的流水下一次在同个地方就看不到了 ,同样是H20,但已经不是之前的那个它了!
抛砖引玉,我们继续讲一下stream的一些方法应用 :
二.方法
1.过滤(filter)
先看这样一段代码
Stream stm = Stream.of("test", "123", "3");
stm.filter(obj -> {
System.out.println(obj);
return true;
});
这段代码最后什么都没输出,在打印日志这里打断点,我们程序也不会进入。
相反,如果做下些许修改,如下:
Stream stm = Stream.of("test", "123", "3");
stm.filter(obj -> {
System.out.println(obj);
return true;
}).collect(Collectors.toList());
则最后会打印出
test 123 3
断点有效
再做些修改,如下:
Stream stm = Stream.of("test", "123", "3");
stm.filter(obj -> {
System.out.println(obj);
return true;
}).forEach(item -> System.out.println(item));
则最后会打印出
test test 123 123 3 3
断点有效
filter方法的参数是Predicate类型,forEach方法的参数是Consumer类型,它们都是函数接口。
如果不添加forEach等Consumer类型,仅仅Predicate,并不会生效,自然不会进入到方法中,编译不会报错,但运行无效。
2.限制条数(limit)
看到limit,首先就想到mysql中的limit取条数sql,整个开发语言中,无非就那么点单词,大同小异。
在stream流中,limit是一种常用方法,用于截取流中前n个的元素。
对应源码中的是:
Stream<T> limit(long maxSize);
注意 maxSize 不允许为负数,虽然编译无错,但运行中抛出 IllegalArgumentException 异常
先看下基本用法:
Stream stm = Stream.of("test", "123", "3");
stm.limit(2).forEach(obj -> System.out.println(obj));
打印结果:
test 123
3.跳过(skip)
skip,玩游戏的时候,看一些剧情,会有skip这样的字样,顾名思义,在stream流操作中,就是跳过前n个的意思。
它的用法跟limit一样,不允许为负数,对应源码中的是:
Stream<T> skip(long n);
示例用法:
Stream stm = Stream.of("test", "123", "3");
stm.skip(2).forEach(obj -> System.out.println(obj));
打印结果:
3
4.去重(distinct)
又是一个类数据库的方法名,在jdk8前,我们对一个集合比如list进行去重,会想到用set,因其自身的不允许重复的特性应用到去重操作中,但要list转化为set,还要写一些遍历方法,烦。jdk8太懂程序员的想法了,提供了distinct的方法。
其源码为:
Stream<T> distinct();
示例用法:
Stream stm = Stream.of("test", "123", "3", "3");
stm.distinct().forEach(obj -> System.out.println(obj));
打印结果:
test 123 3
- 先到此,之后再写写函数式编程