在Jdk1.8中引入了stream流的概念,这个“流”并不同于IO中的输入和输出流,它是Jdk中的一个类:java.util.stream.Stream,使用流进行处理可以帮助我们提升代码的性能。
1. 常见应用
使用Stream流对集合进行过滤和遍历操作:
import java.util.ArrayList;
import java.util.List;
public class Demo{
public static void main(String[]args){
List<String> list =new ArrayList<>();
list.add("张三");
list.add("李四");
list.add("王五五");
list.add("张强");
list.add("张无忌");
list.stream()
//只要以"张"开头的
.filter(s‐>s.startsWith("张"))
//只要长度为3的
.filter(s‐>s.length()==3)、
//forEach中传入Consumer接口的实现类,使用Lambda表达式的方法引用写法
.forEach(System.out::println);
}
}
2. 流的获取
2.1 Stream.of()
java.util.stream.Stream<T>
是Java8新加入的最常用的流接口。(这并不是一个函数式接口。)
获取一个流非常简单,有以下几种常用的方式:
所有的Collection集合都可以通过stream默认方法获取流;
Stream
接口的静态方法of
可以获取数组对应的流。
//获取数组的流
int[] array = new int[10];
Stream<int[]> arrayStream = Stream.of(array);
//获取List的流,当然,对于集合的流有更简单的获取方式
List<String> list = new ArrayList<>();
Stream<List<String>> listStream = Stream.of(list);
2.2 XXX.stream()
Collection获取流
java.util.Collection接口中加入了default方法stream用来获取流,所以其所有实现类均可获取流。
public static void main(String[]args){
List<String> list = new ArrayList<>();
//...
Stream<String> stream1 = list.stream();
Set<String> set = new HashSet<>();
//...
Stream<String> stream2 = set.stream();
Vector<String> vector=new Vector<>();
//...
Stream<String> stream3 = vector.stream();
}
Map获取流
java.util.Map接口不是Collection的子接口,且其K-V数据结构不符合流元素的单一特征,所以获取对应的流需要分key、value或entry等情况:
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();
}
3. 流的常用方法
流模型的操作方法很多,这里介绍一些常用的。这些方法可以被分成两种:
- 延迟方法:返回值类型仍然是Stream接口自身类型的方法,因此支持链式调用。(除了终结方法外,其余方法均为延迟方法。)
- 终结方法:返回值类型不再是Stream接口自身类型的方法,不能再链式调用。
3.1 forEach
void forEach(Consumer<?superT> action);
该方法接收一个Consumer接口函数,会将每一个流元素交给该函数进行处理。
可以用实现类的方式重写Consumer接口的accep方法实现消费逻辑,更简单的方式是直接使用Lambda表达式:
public static void main(String[]args){
Stream<String> stream = Stream.of("aaa","bbb","ccc");
stream.forEach(name ‐> System.out.println(name));
}
还可以利用方法引用进一步简化Lambda表达式
stream.forEach(System.out :: println);
方法引用简化规则解析请参考另一篇博客:
链接: JAVA Lambda表达式使用方法引用优化.
3.2 filter
Stream<T> filter(Predicate<?superT> predicate);
该接口接收一个Predicate函数式接口,可以实现该接口并重写test方法实现过滤逻辑。test()
方法将会产生一个boolean值结果,代表指定的条件是否满足。如果结果为true,那么Stream流的filter方法将会留用元素;如果结果为false,那么filter方法将会舍弃元素。
当然简单的写法还是使用Lambda:
public static void main(String[]args){
Stream<String> original = Stream.of("张三","张四","王五");
Stream<String> result = original.filter(s->s.startsWith("张"));
}
3.3 map
map方法可以将流中的元素映射到另一个流中。
<R>Stream<R> map(Function<? super T, ? extends R> mapper);
该接口需要一个Function函数式接口参数,可以将当前流中的T类型数据转换为另一种R类型的流。可以实现该接口并重写apply方法实现映射逻辑。
当然简单的写法还是使用Lambda:
public static void main(String[]args){
Stream<String> original = Stream.of("10","12","18");
Stream<Integer> result = original.map(str‐>Integer.parseInt(str));
}
3.4 count
流提供count方法来获取其中的元素个数。
long count();
直接获取:
public static void main(String[]args){
Stream<String> original = Stream.of("10","12","18");
long n = original.count();
}
3.4 limit
流提供limit方法来获取前几个元素。
Stream<T> limit(long maxSize);
参数是一个long型,如果集合当前长度大于参数则进行截取;否则不进行操作。基本使用:
public static void main(String[]args){
Stream<String> original = Stream.of("10","12","18");
Stream<String> limit = original.limit(2);
System.out.println(limit.count());//结果:2
}
3.5 skip-跳过前几个
Stream<T> skip(long n);
如果流的当前长度大于n,则跳过前n个;否则将会得到一个长度为0的空流。基本使用:
public static void main(String[]args){
Stream<String> original = Stream.of("10","12","18");
Stream<String> limit = original.skip(2);
System.out.println(limit.count());//结果:1
}
3.6 concat-合并流
static<T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
该方法的基本使用代码如下:
import java.util.stream.Stream;
public class Demo{
public static void main(String[]args){
Stream<String> streamA = Stream.of("AAA");
Stream<String> streamB = Stream.of("BBB");
Stream<String> result = Stream.concat(streamA, streamB);
}
}