Java 8 Stream API 基础了解
1.Stream概念
2.数据源
3.中间操作
4.最终操作
5.Stream使用注意事项
1、java.util.stream.Stream概念
*Java API文档解释:支持顺序和并行聚合操作的一系列元素。
*菜鸟教程解释:这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。
从菜鸟教程解释中可以看到两个关键词:中间操作、最终操作。Java API的一包操作就是创建类对象,调用方法,所以Stream(流)的使用大概分为三部分:
数据源(通过数据源获取对应的Stream对象)、中间操作、最终操作。
Stream主要特征:
Pipelining:流水线风格,每个中间操作都会返回流对象本身,这样多个操作就可以可做串连起来的管道,经过管道不同部分时进行不同操作*(官方说法补充:如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。)*
内部迭代:当执行中间操作或最终操作时,Stream会自动将每个数据执行一次中间操作方法。(官方:以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。)
2、数据源
用于生成Stream的数据源有两种:集合(单列、双列集合)、数组
- 获取流
public static void main(String[] args) {
/*
* 1、单列集合Collection:通过调用内部stream()方法获取
*/
ArrayList<String> list =new ArrayList<>();
Collections.addAll(list, "于谦","郭德纲","郭麒麟","孟鹤堂","侯震","岳云鹏","周九良");
Stream<String> stream = list.stream();
stream.forEach(System.out::println);
System.out.println("---------------------------");
/*
* 2、双列集合Map: 通过获取keySet来获取stream,间接获取
*/
Map<String, String> map = new HashMap<String, String>();
map.put("a", "aaaa");
map.put("b", "bbbb");
Stream<String> stream2 = map.keySet().stream();
stream2.forEach(System.out::println);
System.out.println("---------------------------");
/*
* 3、数组,通过Arrays的getStream(数组)获取代表该数据源的stream流
*/
String[] strs = {"11111","22222","33333","44444"};
Stream<String> stream3 = Arrays.stream(strs);
stream3.forEach(System.out::println);
System.out.println("---------------------------");
/*
* 4、Stream还提供了of(...)静态方法和不确定参数形式,来直接获取一批同类型数据源的stream流
*/
Stream<Integer> stream4 = Stream.of(77,88,99);
stream4.forEach(System.out::println);
System.out.println("---------------------------");
}
-
通过代码可以看出,还有一种Stream.of(T … values)不确定参数的方法,该方法可以获取一批同类型的数据源的Stream(流),但实际还是数组,该方法调用的是Arrays.stream()方法,Stream.class源码:
-
而集合使用流的方法都是用的Collection提供的stream()方法。
3、中间操作方法
-
过滤(filter)
方法 说明 Stream filter(Predicate predicate) 返回由与此给定谓词匹配的此流的元素组成的流。 Predicate是函数式接口,可以使用Lambda表达式
public static void main1(String[] args) {
ArrayList<String> list =new ArrayList<>();
Collections.addAll(list, "于谦","郭德纲","郭麒麟","孟鹤堂","侯震","岳云鹏","周九良");
Stream<String> stream = list.stream();//****************************
/*
* 1、过滤,对应方法Stream<T> filter(Predicate<? extends T> p)
*/
//stream.filter(s->s.startsWith("郭")).filter(s->s.length()==3).forEach(System.out::println);
//过滤条件对象--Predicate函数式接口,用Lambda表达式创建对象
Predicate<String> p1 = s->s.startsWith("郭");
Predicate<String> p2 = s->s.length()==3;
//过滤出集合中元素字符串中以“郭”开头,并且长度等于3的元素--and
//stream.filter(p1.and(p2)).forEach(System.out::println);//filter等价于filter(p1).filter(p2)
//过滤出集合中元素字符串中以“郭”开头,或长度等于3的元素--or
stream.filter(p1.or(p2)).forEach(System.out::println);
}
-
截取和跳过(limit、skip)
方法 说明 Stream limit(long maxSize) 截取流的前maxSize个元素形成新的流,
若maxSize大于原流中数据个数,则只截取原流所有,不报错。Stream skip(long n) 跳过前n个数据,用后面的数据作为数据源生成新的Stream(流)
测试代码:
public static void main2(String[] args) {
ArrayList<String> list =new ArrayList<>();
Collections.addAll(list, "于谦","郭德纲","郭麒麟","孟鹤堂","侯震","岳云鹏","周九良");
Stream<String> stream = list.stream();
//截取流中数据0-2(前两个),生成新的流
//Stream<String> stream2 = stream.limit(2);
//stream2.forEach(System.out::println);
//跳过前2个,将后面的数据生成新的流
//Stream<String> stream3 = stream.skip(2);
//stream3.forEach(System.out::println);
//跳过前2个,截取剩余数据的前2个生成新的流
stream.skip(2).limit(2).forEach(System.out::println);
}
-
合并和去重复(concat、distinct)
方法 说明 Stream concat(Stream a, Stream b) 合并a,b两个Stream(流)成一个Stream Stream distinct() 对流中数据去重复后返回新的Stream
测试代码:
public static void main3(String[] args) {
ArrayList<String> list =new ArrayList<>();
Collections.addAll(list, "于谦","郭德纲","郭麒麟","孟鹤堂","侯震","岳云鹏","周九良");
Stream<String> s1 = list.stream().limit(4);
Stream<String> s2 = list.stream().skip(2);
//将s1,s2连个流合并,打印合并后的流中数据
//Stream.concat(s1, s2).forEach(System.out::println);
//将s1,s2连个流合并,打印合并后的流中数据---去除重复数据
Stream.concat(s1, s2).distinct().forEach(System.out::println);
}
-
映射(map)
方法 说明 Stream map(Function mapper) 返回由给定函数应用于此流的元素的结果组成的流。 map(Function f)方法中参数Function是函数式接口,所以可以使用Lambda表达式
测试代码:
public static void main(String[] args) {
ArrayList<String> list =new ArrayList<>();
Collections.addAll(list, "于谦","郭德纲","郭麒麟","孟鹤堂","侯震","岳云鹏","周九良");
list.stream().map(s->s+"666").forEach(System.out::println);
}
4、最终操作
- Stream最终操作方法:
方法 | 说明 |
---|---|
long count() | 返回此流中的元素数。 |
void forEach(Consumer action) | 对此流的每个元素执行操作。 |
collect(Collector collector) | 收集数据并保存到集合 |
- Collector提供的方法:
方法 | 说明 |
---|---|
public static Collector toList() | 把元素收集到List集合中 |
public static Collector toSet() | 把元素收集到Set集合中 |
public static Collector toMap(Function keyMapper,Function valueMapper) | 把元素收集到Map集合中 |
测试代码:
public static void main(String[] args) {
ArrayList<String> list =new ArrayList<>();
Collections.addAll(list, "于谦","郭德纲","郭麒麟","孟鹤堂","侯震","岳云鹏","周九良");
//1、数据遍历(forEach)
//将中间操作后剩余的数据进行打印,也可以直接打印原流
list.stream().filter(s->s.length()==2).forEach(System.out::println);
System.out.println("---------------");
//将println静态方法使用Lambda表达式格式传入
list.stream().forEach(System.out::println);
System.out.println("---------------");
//2、统计数据个数(count),也可对源流操作
long count = list.stream().filter(s->s.startsWith("郭")).count();
System.out.println("姓郭数据个数:"+count);
System.out.println("---------------");
//3、收集数据封装到集合(collect)
List<String> list2 = list.stream().
filter(s->s.startsWith("郭")).
collect(Collectors.toList());//封装到List
System.out.println("List:"+list2);
Set<String> set = list.stream().
filter(s->s.startsWith("郭")).
collect(Collectors.toSet());//封装到Set
System.out.println("Set:"+set);
}
5、Stream(流)使用注意事项:
- Stream是按需操作,并不存储数据,所以不会对源数据造成影响。
- Stream(流)只能进行一次操作,所以在使用时常用链式编程,不声明对象。
- Stream组成有:数据源、0-n个中间操作、最终操作。
- 区分中间操作方法很简单,返回结果是Stream的就是中间操作。
Stream使用流程图
参考博客:
【重学Java】Stream流 - gonghr - 博客园 (cnblogs.com)
JDK8新特性之Stream流学习_zhangjun62的博客-CSDN博客_jdk8stream流