Java 8 新特性 Stream 简介与常用方法
Stream 是什么
Stream
在介绍 Stream 之前,先回顾一下以往的集合操作。比如对于一个 list,需要统计其中大于 3 的数字,可能会使用迭代器写出如下的代码遍历 list 进行统计。
List<Integer> list = Arrays.asList(1,2,3,4,5);
Iterator<Integer> iterator = list.iterator();
int countBigThan3 = 0;
while (iterator.hasNext()) {
Integer value = (Integer) iterator.next();
if(value > 3)
countBigThan3 ++;
}
这种传统的迭代操作被称为外部迭代,
Java 8 为我们带来了 Stream。Stream 是用函数式编程方式在集合类上进行复杂操作的工具。上面的统计代码可以基于 Stream 改写成如下代码。可以看到,代码中几乎只有比较和统计操作,代码简单,逻辑清晰。其中,filter 方法传入了一个 lambda 表达式用于筛选大于 3 的数字,count 方法统计了筛选过数字的个数。
List<Integer> list = Arrays.asList(1,2,3,4,5);
long countBigThan3 = list.stream()
.filter(value -> value > 3)
.count();
Stream 的这种迭代操作被称为内部迭代。应用代码通过 Stream 构建迭代的逻辑,然后在集合代码中完成迭代,应用代码只需要迭代后获取结果即可。在上面的代码中,filter 方法描述了 Stream 的操作但并不立即执行,只有.count执行的时候filter方法才会执行,这样的方法被称为惰性求值方法.(而这得益于Lambda的延迟执行特性。)
获取流
获取一个流非常简单,有以下几种常用的方式:
1.所有的Collection 集合都可以通过stream 默认方法获取流;
2.Stream 接口的静态方法of 可以获取数组对应的流。
3.Map获取流:java.util.Map 接口不是Collection 的子接口,且其K-V数据结构不符合流元素的单一特征,所以获取对应的流需要分key、value或entry等情况:
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;
public class Demo05GetStream {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
// ...
Stream<String> keyStream = map.keySet().stream();
Stream<String> valueStream = map.values().stream();
Stream<Map.Entry<String, String>> entryStream = map.entrySet().stream();
}
}
常用方法
1.延迟方法:返回值类型仍然是Stream 接口自身类型的方法,因此支持链式调用。(除了终结方法外,其余方法均为延迟方法。)
2.终结方法:返回值类型不再是Stream 接口自身类型的方法,因此不再支持类似StringBuilder 那样的链式调用。本小节中,终结方法包括count 和forEach 方法。
forEach:虽然方法名字叫forEach ,但是与for循环中的“for-each”昵称不同。该方法接收一个Consumer 接口函数,会将每一个流元素交给该函数进行处理。
详见:笔记
void forEach(Consumer<? super T> action);
import java.util.stream.Stream;
public class Demo12StreamForEach {
public static void main(String[] args) {
Stream<String> stream = Stream.of("张无忌", "张三丰", "周芷若");
stream.forEach(name‐> System.out.println(name));
}
}
filter:
Stream<T> filter(Predicate<? super T> predicate);
该接口接收一个Predicate 函数式接口参数(可以是一个Lambda或方法引用)作为筛选条件。
import java.util.stream.Stream;
public class Demo07StreamFilter {
public static void main(String[] args) {
Stream<String> original = Stream.of("张无忌", "张三丰", "周芷若");
Stream<String> result = original.filter(s ‐> s.startsWith("张"));
}
}
映射:map
如果需要将流中的元素映射到另一个流中,可以使用map 方法
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
该接口需要一个Function 函数式接口参数,可以将当前流中的T类型数据转换为另一种R类型的流。
Stream流中的map 方法基本使用的代码如:
import java.util.stream.Stream;
public class Demo08StreamMap {
public static void main(String[] args) {
Stream<String> original = Stream.of("10", "12", "18");
Stream<Integer> result = original.map(str‐>Integer.parseInt(str));
}
}
统计个数:count
正如旧集合Collection 当中的size 方法一样,流提供count 方法来数一数其中的元素个数:
long count();
该方法返回一个long值代表元素个数(不再像旧集合那样是int值)。基本使用:
import java.util.stream.Stream;
public class Demo09StreamCount {
public static void main(String[] args) {
Stream<String> original = Stream.of("张无忌", "张三丰", "周芷若");
Stream<String> result = original.filter(s ‐> s.startsWith("张"));
System.out.println(result.count()); // 2
}
}
取用前几个:limit
limit方法可以对流进行截取,只取用前n个。方法签名:
Stream<T> limit(long maxSize);
参数是一个long型,如果集合当前长度大于参数则进行截取;否则不进行操作。基本使用:
public class Demo10StreamLimit {
public static void main(String[] args) {
Stream<String> original = Stream.of("张无忌", "张三丰", "周芷若");
Stream<String> result = original.limit(2);
System.out.println(result.count()); // 2
}
}
跳过前几个:skip
Stream<T> skip(long n);
如果流的当前长度大于n,则跳过前n个;否则将会得到一个长度为0的空流。基本使用:
public class Demo11StreamSkip {
public static void main(String[] args) {
Stream<String> original = Stream.of("张无忌", "张三丰", "周芷若");
Stream<String> result = original.skip(2);
System.out.println(result.count()); // 1
}
}
组合:concat
如果有两个流,希望合并成为一个流,那么可以使用Stream 接口的静态方法concat :
static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
备注:这是一个静态方法,与java.lang.String 当中的concat 方法是不同的。
public class Demo12StreamConcat {
public static void main(String[] args) {
Stream<String> streamA = Stream.of("张无忌");
Stream<String> streamB = Stream.of("张翠山");
Stream<String> result = Stream.concat(streamA, streamB);
}
}