Java 8 Stream API可以怎么玩?
作者:行百里er
博客:https://chendapeng.cn (opens new window)
提示
这里是 行百里er 的博客:行百里者半九十,凡事善始善终,吾将上下而求索!
Java 8新特性之一 Stream
的官方描述:
Classes in the new
java.util.stream
package provide a Stream API to support functional-style operations on streams of elements.The Stream API is integrated into the Collections API, which enables bulk operations on collections, such as sequential or parallel map-reduce transformations.
Stream是一组用来处理数组、集合的API。
Java 8为什么引入函数式编程呢?简单说为了简洁,效率。
- 函数式编程写出的代码简洁且意图明确,基本告别繁琐的for循环
- 多核友好,函数式编程编写并行执行程序简单,调用一下parallel()方法就好
那么,使用Stream API怎么个简洁法呢?我们先来看一下Stream API给我们提供了哪些方法,然后实际操作一下,看看使用上有是不是真的很爽。
# Stream API主要方法介绍
# long count()
Returns the count of elements in this stream.
返回stream中的元素个数。
# Stream<T> distinct()
Returns a stream consisting of the distinct elements (according to Object.equals(Object)) of this stream.
返回由该流的不同元素(根据 Object.equals(Object) )组成的流。(也就是去重后组成的新的stream)
# Stream<T> filter(Predicate<? super T> predicate)
Returns a stream consisting of the elements of this stream that match the given predicate.
返回由与此给定谓词匹配的此流的元素组成的流。
# Optional<T> findAny()
Returns an Optional describing some element of the stream, or an empty Optional if the stream is empty.
返回描述流的一些元素的Optional如果流为空,则返回一个空的Optional 。
# Optional<T> findFirst()
Returns an Optional describing the first element of this stream, or an empty Optional if the stream is empty.
返回描述此流的第一个元素的可选项,如果流为空,则返回空的可选项。
# void forEach(Consumer<? super T> action)
Performs an action for each element of this stream.
对该流的每个元素执行操作。
# static <T> Stream<T> generate(Supplier<T> s)
Returns an infinite sequential unordered stream where each element is generated by the provided Supplier.
返回无限顺序无序流,其中每个元素由参数Supplier生成 。
生成的是一个无限多元素的流,所以需要其他方法来限制一下。
# static <T> Stream<T> iterate(T seed, UnaryOperator<T> f)
Returns an infinite sequential ordered Stream produced by iterative application of a function f to an initial element seed, producing a Stream consisting of seed, f(seed), f(f(seed)), etc.
返回有序无限连续 Stream由函数的迭代应用产生 f至初始元素 seed ,产生 Stream包括 seed , f(seed) , f(f(seed)) ,等 。
# Stream<T> limit(long maxSize)
Returns a stream consisting of the elements of this stream, truncated to be no longer than maxSize in length.
返回由此流的元素组成的流,截短长度不能超过 maxSize 。
# IntStream mapToInt(ToIntFunction<? super T> mapper)
Returns an IntStream consisting of the results of applying the given function to the elements of this stream.
返回一个 IntStream ,其中包含将给定函数应用于此流的元素的结果。
类似的还有 map()
,mapToDouble()
,mapToLong()
。
# Optional<T> max(Comparator<? super T> comparator)
Returns the maximum element of this stream according to the provided Comparator.
根据提供的 Comparator返回此流的最大元素。
类似的还有 min()
。
# static <T> Stream<T> of(T t)
Returns a sequential Stream containing a single element.
返回包含单个元素的顺序 Stream 。
类似的还有 of(T... values)
。
# Stream<T> skip(long n)
Returns a stream consisting of the remaining elements of this stream after discarding the first n elements of the stream.
在丢弃流的第一个 n元素后,返回由该流的 n元素组成的流。
# Stream<T> peek(Consumer<? super T> action)
Returns a stream consisting of the elements of this stream, additionally performing the provided action on each element as elements are consumed from the resulting stream.
返回由该流的元素组成的流,另外在从生成的流中消耗元素时对每个元素执行提供的操作。
# Stream<T> sorted()
Returns a stream consisting of the elements of this stream, sorted according to natural order.
返回由此流的元素组成的流,根据自然顺序排序。
# Stream<T> sorted(Comparator<? super T> comparator)
Returns a stream consisting of the elements of this stream, sorted according to the provided Comparator.
返回由该流的元素组成的流,根据提供的 Comparator进行排序。
# Object[] toArray()
Returns an array containing the elements of this stream.
返回一个包含此流的元素的数组。
# 玩一玩Stream API
关于Stream API,一般情况下是结合 Lambda表达式
来使用的。我们来看一下具体怎么操作。
# Stream的创建
- 通过数组创建一个Stream
Arrays.stream(array)
/**
* 通过数组创建Stream
*/
static void generateStreamByArray(){
String[] str = {"行", "百", "里", "er"};
Stream<String> arrStream = Arrays.stream(str);
arrStream.forEach(System.out::print);
}
2
3
4
5
6
7
8
可以通过 stream.forEach(System.out::print)
这种 Lambda 表达式的方式打印 Stream 中的每个元素,运行结果:
行百里er
Process finished with exit code 0
2
- 通过集合创建一个Stream
list.stream()
static void generateStreamByList(){
List<String> list = Arrays.asList("行", "百", "里", "er");
list.stream().forEach(System.out::print);
//list.forEach(System.out::print);
}
2
3
4
5
- 通过 generate 方法创建Stream
Stream.generate(Supplier<T> s)
generate方法的参数是一个 Supplier
接口,这个接口的描述文档是:
说明 Supplier
是一个函数式接口,只有一个方法 get()
,因此我们可以用 Lambda 表达式来搞:
//Stream.generate
static void withGenerate() {
Supplier<String> s = () -> "行百里er";
//用limit方法限制生成的个数
Stream.generate(s).limit(3).forEach(System.out::println);
System.out.println("-----------------华丽的分割线-------------");
Stream<String> stream = Stream.generate(() -> "行百里er");
//用limit方法限制生成的个数
stream.limit(3).forEach(System.out::println);
}
2
3
4
5
6
7
8
9
10
11
12
运行结果:
行百里er
行百里er
行百里er
-----------------华丽的分割线-------------
行百里er
行百里er
行百里er
Process finished with exit code 0
2
3
4
5
6
7
8
9
- 通过iterate方法创建Stream
Stream.iterate(seed, UnaryOperator<T>)
通过调用 Stream 的 iterate
方法也能生成一个stream,该方法有2个参数,第一个参数是种子,专门为第二个参数 UnaryOperator
提供初始值,那么 UnaryOperator
又是个什么鬼呢?来看源码:
@FunctionalInterface
public interface UnaryOperator<T> extends Function<T, T> {
/**
* Returns a unary operator that always returns its input argument.
*
* @param <T> the type of the input and output of the operator
* @return a unary operator that always returns its input argument
*/
static <T> UnaryOperator<T> identity() {
return t -> t;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
说明 UnaryOperator
也是函数式的,表示对单个操作数执行的操作,该操作产生与其操作数类型相同的结果。
这是专门针对 操作数和对操作结果类型相同
这一类的。
比如我要产生一个stream,给定种子元素为2,产生3个元素,每个元素是是前一个元素的2倍,可以这样来:
//Stream.iterate(seed, UnaryOperator)
static void withIterate() {
UnaryOperator<Integer> u = i -> i * 2;
Stream.iterate(2, u).limit(3).forEach(System.out::println);
System.out.println("---------华丽的分割线-----------");
//等价于下面这种写法
Stream.iterate(2, i -> i * 2).limit(3).forEach(System.out::println);
}
2
3
4
5
6
7
8
9
执行结果:
2
4
8
---------华丽的分割线-----------
2
4
8
Process finished with exit code 0
2
3
4
5
6
7
8
9
- 其他方式创建Stream
字符串的 chars()
方法也能生成stream,不过是 IntStream
:
static void others() {
IntStream chars = "行百里er".chars();
//打印的是int类型
chars.forEach(System.out::println);
}
2
3
4
5
运行结果:
34892
30334
37324
101
114
2
3
4
5
要想按照字符输出,做一下强制转换就行了:
static void others() {
IntStream chars = "行百里er".chars();
//打印的是int类型
//chars.forEach(System.out::println);
//可以将int转成char输出
chars.forEach((c) -> System.out.print((char) c));
}
2
3
4
5
6
7
行百里er
Process finished with exit code 0
2
# 还有这种操作?
# 输出一个集合中所有的偶数
一句话搞定:
Arrays.asList(11, 2, 3, 4, 5, 19, 8, 18, 23).stream().filter((x) -> x % 2 == 0).forEach(System.out::println);
filter
方法的参数 Predicate<T>
也是函数式接口, 代表一个谓词(boolean-valued函数)的一个参数 。
# 计算一个集合中所有偶数的和
int sum = Arrays.asList(11, 2, 3, 4, 5, 19, 8, 18, 23).stream().filter((x) -> x % 2 == 0).mapToInt(x -> x).sum();
这里要注意的是,filter
出偶数之后要转成 IntStream
,才有 sum()
方法,用 mapToInt(ToIntFunction<? super T> mapper)
转即可。
参数ToIntFunction:表示产生值为int的结果的函数。这是专门为int- production转换提供的方法。
# 输出集合中的最大值、最小值
用 max()
,min()
方法:
List<Integer> intList = Arrays.asList(11, 2, 3, 4, 5, 6);
//输出最大值
Optional<Integer> maxOptional = intList.stream().max(Comparator.comparingInt(x -> x));
Integer max = maxOptional.get();
System.out.println("max:" + max);
//输出最小值
System.out.println(intList.stream().min(Comparator.comparingInt(x -> x)).get());
2
3
4
5
6
7
8
不用 max
,min
也能实现,我们可以先排好序:
//输出最小值,先排好序,sorted不传参默认升序排序,找到第一个就是最小的了
System.out.println(intList.stream().sorted().findFirst().get());
//按照降序排序,再找第一个就是最大的了
System.out.println(intList.stream().sorted((x, y) -> y - x).findFirst().get());
2
3
4
5
# 集合去重
使用 distinct
方法或转成 Set
:
//去重
Stream<Integer> distinctStream = Arrays.asList(1, 1, 2, 3, 5, 5, 3).stream().distinct();
distinctStream.forEach(System.out::println);
//去重方法二:转成Set
Set<Integer> collect = Arrays.asList(1, 1, 2, 3, 5, 5, 3).stream().collect(Collectors.toSet());
collect.forEach(System.out::println);
2
3
4
5
6
# 输出一个区域内的数字
比如,构造一个集合,里面存放1 ~ 50数字,要求输出21 ~ 30这10个数。
可以使用 iterate
方法构造stream,用 limit
限制为50个,然后调用 skip
方法跳过前20个,这时会返回一个从21 ~ 50的stream,我们只需在limit一下就可以满足只有20 ~ 30的stream了:
//输出21~30
Stream<Integer> stream = Stream.iterate(1, x -> x + 1).limit(50).skip(20).limit(10);
stream.forEach(System.out::println);
2
3
# 小结
- Stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的 Iterator。
原始版本的 Iterator,只能显式地一个一个遍历元素并对其执行某些操作;
高级版本的 Stream,只要给出需要对其包含的元素执行什么操作,比如 “过滤掉长度大于 10 的字符串”、“获取每个字符串的首字母”等,Stream 会隐式地在内部进行遍历,做出相应的数据转换。
- Stream不支持索引访问
- Stream很容易生成数组或集合
- Stream支持过滤,查找,转换,汇总,聚合等操作
首发公众号 行百里er ,欢迎老铁们关注阅读指正。