核心概念:
一个流的操作 = 创建流 + 中间操作 + 结果转换
1)创建流:指从集合转换过滤,数值转换过滤、I/O转换、创建等等;
2)中间操作:指对流中的数据进行聚合,如filter\map\sorted\reduce、distinct等等;
3)结果转换:指将流进行输出,打印、转换成array、转换成collection等;
特点:
1)流是不存储值的,只是一个管道过程
2)单个流只能被使用一次,再次被使用报错
3)stream具有延迟执行特性,只有调用终端操作时,中间操作才会执行
public interface BaseStream> extends AutoCloseable {
Iterator iterator();
Spliterator spliterator();
boolean isParallel();
S sequential();
S parallel();
S unordered();
S onClose(Runnable closeHandler);
@Override
void close();
}
方法详述:
1)Iterator iterator(); 获得流的迭代器,并返回对该迭代器的引用(终端操作)
2)Spliterator spliterator(); 获取流的spliterator,并返回其引用(终端操作)
3)boolean isParallel(); 如果调用流是一个并行流,则返回true;如果调用流是一个顺序流,则返回false
4)S sequential(); 基于调用流,返回一个顺序流。如果调用流已经是顺序流了,就返回该流(中间操作)
5)S parallel(); 基于调用流,返回一个并行流。如果调用流已经是并行流了,就返回该流(中间操作)
6)S unordered(); 基于调用流,返回一个无序流。如果调用流已经是无序流了,就返回该流(中间操作)
7)S onClose(Runnable closeHandler); 基于调用流,返回一个并行流。如果调用流已经是并行流了,就返回该流(中间操作)
8)void close(); 从AutoCloseable继承来的,调用注册关闭处理程序,关闭调用流(很少会被使用到)
方法 | 描述 |
---|---|
Collection.stream() |
使用一个集合的元素创建一个流。 |
Stream.of(T...) |
使用传递给工厂方法的参数创建一个流。 |
Stream.of(T[]) |
使用一个数组的元素创建一个流。 |
Stream.empty() |
创建一个空流。 |
Stream.iterate(T first, BinaryOperator |
创建一个包含序列 first, f(first), f(f(first)), ... 的无限流 |
Stream.iterate(T first, Predicate |
(仅限 Java 9)类似于 Stream.iterate(T first, BinaryOperator ,但流在测试预期返回 false 的第一个元素上终止。 |
Stream.generate(Supplier |
使用一个生成器函数创建一个无限流。 |
IntStream.range(lower, upper) |
创建一个由下限到上限(不含)之间的元素组成的 IntStream 。 |
IntStream.rangeClosed(lower, upper) |
创建一个由下限到上限(含)之间的元素组成的 IntStream 。 |
BufferedReader.lines() |
创建一个有来自 BufferedReader 的行组成的流。 |
BitSet.stream() |
创建一个由 BitSet 中的设置位的索引组成的 IntStream 。 |
Stream.chars() |
创建一个与 String 中的字符对应的 IntStream 。 |
常用创建方式:
import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class StreamTest {
public static void main(String[] args) {
//通过Arrays创建
int[] arr = new int[]{1,2,3,4,5};
IntStream intStream = Arrays.stream(arr);
intStream.forEach(System.out::println);
//通过Stream.of创建
Stream stream1 = Stream.of("dfd","erw","ewfe");
stream1.forEach(System.out::println);
//通过 Collections集合创建
List strs = Arrays.asList("2324","dsgfdg","343","ere");
//创建普通流
Stream stream3 = strs.stream();
//创建并行流
Stream stream4 = strs.parallelStream();
}
}
注意:Stream操作的对象只能是引用类型的,不能用于基本类型,因此对于基本数值型官方提供了三种对应的包装类型IntStream、LongStream、DoubleStream。
流的中间操作,可以为分无状态操作
和有状态操作
两种,在无状态操作中,在处理流中的元素时,会对当前的元素进行单独处理。比如:谓词过滤操作,因为每个元素都是被单独进行处理的,所有它和流中的其它元素无关,因此被称为无状态操作;而在有状态操作中,某个元素的处理可能依赖于其他元素。比如查找最小值,最大值,和排序,因为他们都依赖于其他的元素。因此为称为有状态操作。当需要进行并行处理流时,有状态的操作和无状态的区别是非常重要的,因为有状态操作可能需要几次处理才能完成,
流的筛选,即filter,是按照一定的规则校验流中的元素,将符合条件的元素提取出来的操作。filter通常要配合collect(收集),将筛选结果收集成一个新的集合。
流的匹配,与筛选类似,也是按照规则提取元素,不同的是,匹配返回的是单个元素或单个结果。
public static void main(String[] args) {
//引用类型
List person= new ArrayList();
person.add(new Person("Tom", 8900));
person.add(new Person("Jack", 7000));
person.add(new Person("Lily", 9000));
person.add(new Person("Lily", 9000));
List collect = person.stream().filter(x -> x.getSalary() > 8000).collect(Collectors.toList());
//打印结果:streamTest.Person@214c265e
//streamTest.Person@448139f0
//基本类型
List list = Arrays.asList(1,3,6,7,8);
list.stream().filter(x -> x > 6 && x < 8).forEach(System.out::println);
//打印结果:7
list.stream().distinct().forEach(System.out::print);
//打印结果:1,3,6,7,8
list.stream().limit(2).forEach(System.out::print);
//打印结果:1,3
list.stream().skip(3).forEach(System.out::print);
//打印结果:7,8,8
}
在stream中,针对流进行计算后得出结果,例如求和、求最值等,这样的操作被称为聚合操作。聚合操作在广义上包含了max、min、count等方法和reduce、collect。
1、获取String集合中最长的元素
public static void main(String[] args) {
List list = Arrays.asList("adnm","admmt","pot");
Optional max = list.stream().max(Comparator.comparing(String::length));
System.out.println(max);
}
// 预期结果:
Optional[admmt]
2、对象集合最值
public static void main(String[] args) {
List person= new ArrayList();
person.add(new Person("Tom", 8900));
person.add(new Person("Jack", 7000));
person.add(new Person("Lily", 9000));
person.add(new Person("Lily", 9000));
Optional max = person.stream().max(Comparator.comparingInt(Person::getSalary));
System.out.println(max.get().getSalary());//9000
long count = person.stream().filter(x -> x.getSalary() > 8000).count();
System.out.println(count);//2
}
Stream流中,map可以将一个流的元素按照一定的映射规则映射到另一个流中。
public static void main(String[] args) {
List personList = new ArrayList();
personList.add(new Person("Tom", 8900));
personList.add(new Person("Jack", 7000));
personList.add(new Person("Lily", 9000));
// 为节省篇幅,personList复用了演示数据中的personList
personList.stream().map(person -> person.getSalary()).forEach(System.out::println);
//打印结果: 8900,7000,9000
}
顾名思义,缩减操作,就是把一个流缩减成一个值,比如对一个集合求和、求乘积等。
Stream流定义了三个reduce:
public interface Stream extends BaseStream> {
// 方法1
T reduce(T identity, BinaryOperator accumulator);
// 方法2
Optional reduce(BinaryOperator accumulator);
// 方法3
U reduce(U identity,
BiFunction accumulator,
BinaryOperator combiner);
}
前两种缩减方式:
第一种缩减方法接收一个BinaryOperator accumulator function(二元累加计算函数)和identity(标示值)为参数,返回值是一个T类型(代表流中的元素类型)的对象。accumulator代表操作两个值并得到结果的函数。identity按照accumulator函数的规则参与计算,假如函数是求和运算,那么函数的求和结果加上identity就是最终结果,假如函数是求乘积运算,那么函数结果乘以identity就是最终结果。
第二种缩减方式不同之处是没有identity,返回值是Optional(JDK8新类,可以存放null)。
普通集合求和、求最值
public static void main(String[] args) throws Exception {
List list = Arrays.asList(1, 3, 2);
// 求和
Integer sum = list.stream().reduce(1, (x, y) -> x + y);
// 结果是7,也就是list元素求和再加上1
System.out.println(sum);
// 写法2
Integer sum2 = list.stream().reduce(1, Integer::sum);
System.out.println(sum2); // 结果:7
// 求最值
Integer max = list.stream().reduce(6, (x, y) -> x > y ? x : y);
System.out.println(max); // 结果:6
// 写法2
Integer max2 = list.stream().reduce(1, Integer::max);
System.out.println(max2); // 结果:3
}
对象集合求和、求最值:
public class MyTest {
public static void main(String[] args) {
List personList = new ArrayList();
personList.add(new Person("Tom", 8900));
personList.add(new Person("Jack", 7000));
personList.add(new Person("Lily", 9000));
// 求和
// 预期结果:Optional[24900]
System.out.println(personList.stream().map(Person::getSalary).reduce(Integer::sum));
// 求最值-方式1
Person person = personList.stream().reduce((p1, p2) -> p1.getSalary() > p2.getSalary() ? p1 : p2).get();
// 预期结果:Lily:9000
System.out.println(person.getName() + ":" + person.getSalary());
// 求最值-方式2
// 预期结果:Optional[9000]
System.out.println(personList.stream().map(Person::getSalary).reduce(Integer::max));
// 求最值-方式3:
System.out.println(personList.stream().max(Comparator.comparingInt(Person::getSalary)).get().getSalary());
}
}
第三种缩减操作:
第三种缩减操作接收三个参数:标示值(identity)、二元操作累加器(BiFunction accumulator)、二元组合方法(BinaryOperator<.U> combiner)。其中combiner只有在并行
下面用对象集合求和和求最大值的实例来演示第三种缩减操作的用法:
public static void main(String[] args) {
List personList = new ArrayList();
personList.add(new Person("Tom", 8900));
personList.add(new Person("Jack", 7000));
personList.add(new Person("Lily", 9000));
// 求和-方式1
Integer sumSalary = personList.stream().reduce(0, (sum, p) -> sum += p.getSalary(),
(sum1, sum2) -> sum1 + sum2);
System.out.println(sumSalary); // 24900
// 求和-方式2
Integer sumSalary2 = personList.stream().reduce(0, (sum, p) -> sum += p.getSalary(), Integer::sum);
System.out.println(sumSalary2); // 24900
// 求最大值-方式1
Integer maxSalary = personList.stream().reduce(0, (max, p) -> max > p.getSalary() ? max : p.getSalary(),Integer::max);
System.out.println(maxSalary); // 9000
// 求最大值-方式2
Integer maxSalary2 = personList.stream().reduce((max, p) -> max > p.getSalary() ? max : p.getSalary(),(max1, max2) -> max1 > max2 ? max1 : max2);
System.out.println(maxSalary2); // 9000
}
Sorted方法是对流进行排序,并得到一个新的stream流,是一种中间操作。Sorted方法可以使用自然排序或特定比较器
public static void main(String[] args) {
String[] strArr = { "ab", "bcdd", "defde", "ftr" };
// 1、按长度自然排序,即长度从小到大
Arrays.stream(strArr).sorted(Comparator.comparing(String::length)).forEach(System.out::println);
// 2、按长度倒序,即长度从大到小
Arrays.stream(strArr).sorted(Comparator.comparing(String::length).reversed()).forEach(System.out::println);
// 3、首字母倒序
Arrays.stream(strArr).sorted(Comparator.reverseOrder()).forEach(System.out::println);
// 4、首字母自然排序
Arrays.stream(strArr).sorted(Comparator.naturalOrder()).forEach(System.out::println);
}
/**
* thenComparing
* 先按照首字母排序
* 之后按照String的长度排序
*/
@Test
public void testSorted3_(){
Arrays.stream(arr1).sorted(Comparator.comparing(this::com1).thenComparing(String::length)).forEa
ch(System.out::println);
}
public char com1(String x){
return x.charAt(0);
}
// 输出结果:
// 1、ftr bcdd defde
// 2、defde bcdd ftr ab
// 3、ftr defde bcdd ab
// 4、ab bcdd defde ftr
会消费流,这种操作会产生一个结果的,比如上面的 iterator()和 spliterator(),以及上一篇中提到的min()和max(),或者是执行某一种操作,比如上一篇的forEach(),如果一个流被消费过了,那它就不能被重用的。
collect操作可以接受各种方法作为参数,将流中的元素汇集,得到
public interface Stream extends BaseStream> {
R collect(Supplier supplier,BiConsumer accumulator,
BiConsumer combiner);
R collect(Collector super T, A, R> collector);
}
观察上面接口定义可知,collect使用Collector作为参数,Collector包含四种不同的操作:supplier(初始构造器), accumulator(累加器), combiner(组合器), finisher(终结者)。实际上,Collectors类内置了很多收集操作。
具体使用方式参考:
https://blog.csdn.net/mu_wind/article/details/91464351#_209