Stream’APi简介
Stream’API是Java8中的新特性,基于Lambda表达式,对Collection(集合)的各种操作有了很大的改变,极大的提升了编码效率和代码的可读性。Stream有串行和并行两种模式,并行模式会自动创建多个线程,使用fork(join)Java7特性,来拆分任务和加速处理过程。Stream是一种类似IO流的东西,但是并不相同,实质是对集合操作的一种高度抽象,而且更重要的是,Stream不是数据结构,是不存储数据的,数据存储在底层的集合中,或者根据需要产生出来(例如Stream进行终端操作的时候,生成新的集合)。
Stream抽象概念
Stream将要处理的数据看作一个流,流在管道中流动,可以在管道的节点上对流进行筛选,排序,聚合等操作,也就是数据流在管道中经过中间操作(intermediate operation),最后由终端操作(terminal operation)来得到前面处理过的数据,可以抽象的看作一种类似通过sql查询数据的方式。
Stream的特征
- Stream不是数据结构,是不存储数据的;
- Stream的构成数据源是集合、数组,I/O channel,generate等等;
- Stream的中间操作会返还流对象本身,这样就形成了一个管道;
- Stream的中间操作类似sql语句,比如filter(筛选), map(元素映射), reduce(聚合), find(查询), match(匹配), sorted(排序)等;
- Stream的迭代跟for和foreach在集合外显式迭代不同,Stream是内部迭代,基于访问者模式(Visitor)实现;
- Stream有串行和并行两种模式;
Stream用法
如何创建Stream流
- stream() 为集合创建串行流。
- parallelStream() 为集合创建并行流。
// 代码示例
// 集合创建流 Collection接口增加了Steam 方法()
List<String> listStr = Arrays.asList("java", "python", "shell", "javaScript");
Stream<String> steamStr = listStr.stream();
steamStr.forEach(str -> System.out.println(str));
Set<String> setStr = ZoneId.getAvailableZoneIds();
Stream<String> steamSetStr = setStr.stream();
steamSetStr.forEach(str -> System.out.println(str));
// 从数组创建流 Arrays类中增加stream(T[] array)方法
String[] strs = {"java", "python", "shell", "javaScript"};
Stream<String> stringStream = Arrays.stream(strs);
// 从静态方法中创建
Stream<String> staticStream1 = Stream.of("java", "python", "shell", "javaScript");
staticStream1.forEach(str -> System.out.println(str));
// 使用iterate静态方法(迭代器的方式)创建无限大小的流,需配合limit使用,防止内存溢出
// 会一直增加数据,没有上限
Stream<Integer> staticStream2 = Stream.iterate(0, x -> x + 1).limit(10);
staticStream2.forEach(integer -> System.out.println(integer));
// 使用generate静态方法创建无限大小的流,需要配合limit使用,防止内存溢出
// generate方式创建的无限流最大值是Long.MAX_VALUE
Random random = new Random();
Stream<Integer> staticStream3 = Stream.generate(() -> random.nextInt()).limit(10);
staticStream3.forEach(integer -> System.out.println(integer));
// 其他方式
java.io.BufferedReader.lines()
java.util.stream.IntStream.range()
java.nio.file.Files.walk()
java.util.Spliterator
Random.ints()
BitSet.stream()
Pattern.splitAsStream(java.lang.CharSequence)
JarFile.stream()
Stream流的中间操作
该表的作用是Stream流中间操作方法的参数类型
函数式接口名 | 作用 |
---|---|
Function<T, R> | 接受一个参数T,返回结果R |
Predicate |
接受一个参数T,返回boolean |
Supplier |
不接受任何参数T,返回结果T |
Consumer |
接受一个参数T,不返回结果 |
UnaryOperator |
继承自Function<T,T>,返回相同类型T的结果 |
BiFunction<T, U, R> | 接收两个参数T,U,返回结果R |
BinaryOperator |
继承自BiFunction<T,T,T>,返回相同类型T的结果 |
Runnable | 实际上不接受任何参数,也不返回结果 |
Comparable |
实际上是接受两个相同类型的T,返回int |
Callable |
不接受任何参数,返回结果V |
过滤操作
- filter() 将符合条件的所有元素(数据)转移到新流中。filter的参数类型是Predicate
// 代码示例
List<String> listStr = Arrays.asList("java", "python", "shell", "javaScript");
Stream<String> steamStr = listStr.stream();
steamStr.filter(s -> s.startsWith("j")).forEach(s -> System.out.println(s));
转换操作
- map() 将所有数据经过处理之后(可以改变对象类型)转移到新流中。map的参数类型是Function<T,R>
- flatMap(Function<? super T, ? extends Stream<? extends R>> mapper) 接收一个泛型参数,另一个为Stream流参数,返回的是泛型R,作用是将两个流合并成一个流输出
// 代码示例
List<String> listStr = Arrays.asList("java", "python", "shell", "javaScript");
Stream<String> steamStr = listStr.stream();
steamStr.map(s -> s.substring(2)).forEach(s -> System.out.println(s));
String[] strs = { "aaa", "bbb", "ccc" };
Arrays.stream(strs).map(str -> str.split("")).forEach(System.out::println);
// [Ljava.lang.String;@5cc7c2a6
// [Ljava.lang.String;@b97c004
// [Ljava.lang.String;@4590c9c3
Arrays.stream(strs).map(str -> str.split("")).flatMap(str -> Arrays.stream(str)).forEach(System.out::println);
// a/a/a/b/b/b/c/c/c
// map操作将strs拆分为三个数组,stream流中的元素由stream<String>变成了stream<String[]>
// flatMap操作将三个stream<String[]>流合并成一个stream<String>流
提取操作
- skip(long n) 忽略流中的前n个元素(数据)
- limit(long maxSize) 获取流中的maxSize个元素(数据)
// 代码示例
List<String> listStr = Arrays.asList("java", "python", "shell", "javaScript");
Stream<String> steamStr = listStr.stream();
steamStr.skip(2).forEach(s -> System.out.println(s));
steamStr.limit(2).forEach(s -> System.out.println(s));
去重操作
- distinct() 去除流中重复的元素
// 代码示例
// 运行时记得注释其中stringStream执行语句否则会有异常
// java.lang.IllegalStateException: stream has already been operated upon or closed
Stream<String> stringStream = Arrays.asList("java", "python", "shell", "javaScript", "java").stream();
stringStream.distinct().forEach(s -> System.out.println(s));
stringStream.forEach(s -> System.out.println(s));
排序操作
- sort() 将流中的元素(数据)排序
- sort(Comparator<? super T> comparator) 将流中元素根据属性排序
// 代码示例
public class test {
public static List<Emp> emps = new ArrayList<>(10);
static {
emps.add(new Emp("1", "xiaoHong1", 20));
emps.add(new Emp("1", "xiaoHong2", 202));
emps.add(new Emp("1", "xiaoHong3", 32));
emps.add(new Emp("1", "xiaoHong4", 45));
emps.add(new Emp("1", "xiaoHong5", 17));
emps.add(new Emp("1", "xiaoHong6", 14));
emps.add(new Emp("1", "xiaoHong7", 65));
emps.add(new Emp("1", "xiaoHong8", 38));
}
public static class Emp {
private String code;
private String name;
private int age;
public Emp(String code, String name, int age) {
this.code = code;
this.name = name;
this.age = age;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
/**
* @param empList 输出集合中的元素
*/
public static void println(List<Emp> empList) {
empList.stream().forEach(emp -> System.out.println(String.format("编号 %S , 姓名 %s , 年龄 %s", emp.getCode(), emp.getName(), emp.getAge())));
}
/**
* 根据对象中年龄进行排序
*/
public static List<Emp> sortByAge(List<Emp> empList) {
return empList.stream().sorted(Comparator.comparing(emp -> emp.getAge())).collect(Collectors.toList());
}
/**
* 取出对象中姓名排序,并只返回包含姓名的集合
*/
public static List<String> sortByName(List<Emp> empList) {
return empList.stream().map(emp -> emp.getName()).sorted().collect(Collectors.toList());
}
public static void main(String[] args) {
println(emps);
println(sortByAge(emps));
sortByName(emps).stream().forEach(s -> System.out.println(s));
}
}
操作对象
- peek(Consumer<? super T> action)
// 代码示例
/**
* @param empList 输出集合中的元素
*/
public static void println(List<Emp> empList) {
empList.stream().forEach(emp -> System.out.println(String.format("编号 %S , 姓名 %s , 年龄 %s", emp.getCode(), emp.getName(), emp.getAge())));
}
/**
* 给年龄大于30岁增加十岁,并返回筛选后的集合
*/
public static List<Emp> addAge(List<Emp> empList) {
return empList.stream().filter(emp -> emp.getAge() > 30).peek(emp -> emp.setAge(emp.getAge() + 10)).collect(Collectors.toList());
}
public static void main(String[] args) {
println(addAge(emps));
}
聚合操作
- reduce方法
- reduce(BinaryOperator<T> accumulator)
接收一个参数,且参数类型为函数式接口的计算规则,初始值是List集合的第一个值
- reduce(T identity, BinaryOperator<T> accumulator)
参数类型为函数式接口的计算规则,第一参数值是初始值,第二个参数是函数式接口的计算规则
- reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner)
在多线程中使用的,具体不知道(https://segmentfault.com/q/1010000004944450)
// 代码示例
// 将流中元素归约为一个值
Random random = new Random();
List<Integer> randomList = Stream.generate(() -> random.nextInt(10)).limit(10).collect(Collectors.toList());
randomList.stream().forEach(integer -> System.out.println(integer));
System.out.println(randomList.stream().reduce((a, b) -> a + b).get());
System.out.println(randomList.stream().reduce(1,(a, b) -> a + b).longValue());
List<Integer> iterateList = Stream.iterate(0, x -> x + 1).limit(10).collect(Collectors.toList());
iterateList.stream().forEach(integer -> System.out.println(integer));
System.out.println(iterateList.stream().reduce((a, b) -> a - b).get());
System.out.println(iterateList.stream().reduce(1,(a, b) -> a - b).longValue());
collect方法
```javacollect(Supplier
supplier,BiConsumer<R, ? super T> accumulator,BiConsumer<R, R> combiner);
传入三个参数的抽象方法collect(Collector<? super T, A, R> collector);
该方法只有一个参数,这个参数先看下stream中的collect操作Collectors静态工厂类,在这个静态工厂类里面,大多都使用三个参数的collect方法实现的。
```
具体的实现类方法
// 代码示例
public class test {
public static List<Emp> emps = new ArrayList<>(10);
static {
emps.add(new Emp("1", "xiaoHong1", 20));
emps.add(new Emp("1", "xiaoHong2", 202));
emps.add(new Emp("2", "xiaoHong3", 32));
emps.add(new Emp("1", "xiaoHong4", 45));
emps.add(new Emp("2", "xiaoHong5", 17));
emps.add(new Emp("1", "xiaoHong6", 14));
emps.add(new Emp("3", "xiaoHong7", 65));
emps.add(new Emp("3", "xiaoHong8", 38));
}
public static class Emp {
private String code;
private String name;
private int age;
public Emp(String code, String name, int age) {
this.code = code;
this.name = name;
this.age = age;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public static void main(String[] args) {
//转List
List<String> nameList = emps.stream().map(emp -> emp.getName()).collect(Collectors.toList());
//转Set
Set<String> nameSet = emps.stream().map(emp -> emp.getName()).collect(Collectors.toSet());
//转map,需指定key值,Function.identity()指的是当前对象本身
Map<String, Emp> nameMap1 = emps.stream().collect(Collectors.toMap(emp -> emp.getName(), emp -> emp));
Map<String, Emp> nameMap2 = emps.stream().collect(Collectors.toMap(emp -> emp.getName(), Function.identity()));
//计算集合中元素个数
long count = emps.stream().collect(Collectors.counting());
//计算数据 summarizingInt summarizingDouble summarizingLong
IntSummaryStatistics sum = emps.stream().collect(Collectors.summarizingInt(emp -> emp.getAge()));
//平均数
System.out.println(sum.getAverage());
//总数
System.out.println(sum.getCount());
//最大值
System.out.println(sum.getMax());
//最小值
System.out.println(sum.getMin());
//求和
System.out.println(sum.getSum());
//连接字符串 前缀和后缀只会出现在整个字符串的首尾
String nameStr1 = emps.stream().map(emp -> emp.getName()).collect(Collectors.joining());
String nameStr2 = emps.stream().map(emp -> emp.getName()).collect(Collectors.joining("中间-"));
String nameStr3 = emps.stream().map(emp -> emp.getName()).collect(Collectors.joining("中间-", "前缀*", "后缀&"));
System.out.println(nameStr1);
System.out.println(nameStr2);
System.out.println(nameStr3);
//最大值 maxBy 最小值 minBy
Optional<Integer> maxAge = emps.stream().map(emp -> emp.getAge()).collect(Collectors.maxBy(Comparator.comparing(emp -> emp)));
Optional<Integer> minAge = emps.stream().map(emp -> emp.getAge()).collect(Collectors.minBy(Comparator.comparing(emp -> emp)));
System.out.println(maxAge.get());
System.out.println(minAge.get());
//聚合操作
Optional<Integer> ageSum1 = emps.stream().map(emp -> emp.getAge()).collect(Collectors.reducing((x, y) -> x + y));
Integer ageSum2 = emps.stream().map(emp -> emp.getAge()).collect(Collectors.reducing(1, (x, y) -> x + y));
System.out.println(ageSum1.get());
System.out.println(ageSum2);
//分组操作 根据地址把原List进行分组
Map<String, List<Emp>> mapGroup = emps.stream().collect(Collectors.groupingBy(emp -> emp.getCode()));
mapGroup.forEach((s, empList) -> mapGroup.get(s).forEach(emp -> System.out.println(s + ":" + emp.getName())));
//分区操作 需要根据类型进行 需要根据类型指定判断分区
Map<Boolean, List<Emp>> mapPartitioning = emps.stream().collect(Collectors.partitioningBy(emp -> emp.getAge() > 20));
mapPartitioning.forEach((b, empList) -> mapPartitioning.get(b).forEach(emp -> System.out.println(b + ":" + emp.getName())));
List<String> listStr1 = Arrays.asList("java", "python", "shell", "javaScript");
List<String> listStr2 = Arrays.asList("javaNew", "pythonNew", "shellNew", "javaScriptNew");
Stream<String> steamSt1 = listStr1.stream();
Stream<String> steamSt2 = listStr2.stream();
Stream.concat(steamSt1, steamSt2).forEach(s -> System.out.println(s));
}
}
stream的终端操作
对集合的流进行遍历
- forEach(Consumer<? super T> action)
- forEachOrdered(Consumer<? super T> action)
// 代码示例 // forEach遍历是无序的遍历 // forEach遍历是按照元素的在流中的顺序进行遍历 List<String> strList = Arrays.asList("aaa","bbb","ccc"); strList.parallelStream().forEach(str-> System.out.println(str)); System.out.println(); strList.parallelStream().forEachOrdered(str-> System.out.println(str));
将流转换为数组
- toArray(IntFunction<A[]> generator)
- toArray()
// toArray()方法的底层调用的还是toArray(IntFunction<A[]> generator)方法
// 代码示例
List<String> strList = Arrays.asList("aaa", "bbb", "ccc");
Object[] o = strList.stream().toArray();
String[] s = strList.stream().toArray(str -> new String[strList.size()]);
//将集合转换为数组进行输出
Arrays.stream(o).forEach(o1 -> System.out.println(o1));
Arrays.stream(s).forEach(s1 -> System.out.println(s1));
流的长度及流中元素(数据)比较
- long count() 计算流的长度
- boolean anyMatch(Predicate<? super T> predicate) 判断条件中,流中任意一个元素符合,返回true
- boolean allMatch(Predicate<? super T> predicate) 判断条件中,流中所有元素都符合,返回true
- boolean noneMatch(Predicate<? super T> predicate) 判断条件中,流中任意一个元素都不符合,返回true
// 代码示例
public class test {
public static List<Emp> emps = new ArrayList<>(10);
static {
emps.add(new Emp("1", "xiaoHong1", 20));
emps.add(new Emp("1", "xiaoHong2", 202));
emps.add(new Emp("1", "xiaoHong3", 32));
emps.add(new Emp("1", "xiaoHong4", 45));
emps.add(new Emp("1", "xiaoHong5", 17));
emps.add(new Emp("1", "xiaoHong6", 14));
emps.add(new Emp("1", "xiaoHong7", 65));
emps.add(new Emp("1", "xiaoHong8", 38));
}
public static class Emp {
private String code;
private String name;
private int age;
public Emp(String code, String name, int age) {
this.code = code;
this.name = name;
this.age = age;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public static void main(String[] args) {
System.out.println(emps.stream().filter(emp -> emp.getAge() > 30).count());
// 5
System.out.println(emps.stream().allMatch(emp -> emp.getAge() > 30));
// false
System.out.println(emps.stream().anyMatch(emp -> emp.getName().startsWith("xiao")));
// true
System.out.println(emps.stream().noneMatch(emp -> emp.getAge() < 10));
// false
}
}
文章借鉴处