Stream流为JDK8新增特性,为数组、集合等批量数据提供了一套函数式操作方法,简化数组和集合操作的API。
这个Stream的流不同于java.io的InputStream和OutputStream,它代表的是任意Java对象的序列。
一个顺序输出的Java对象序列,不就是一个List容器吗?
这个Stream和List也不一样,List存储的每个元素都是已经存储在内存中的某个Java对象,而Stream输出的元素可能并没有预先存储在内存中,而是实时计算出来的。
Stream的工作原理
一个Stream
可以轻易地转换为另一个Stream
,而不是修改原Stream
本身。最后,真正的计算通常发生在最后结果的获取,也就是惰性计算(一个Stream
转换为另一个Stream
时,实际上只存储了转换规则,并没有任何计算发生)。例如:
Stream<BigInteger> naturals = createNaturalStream(); // 不计算
Stream<BigInteger> s2 = naturals.map(BigInteger::multiply); // 不计算
Stream<BigInteger> s3 = s2.limit(100); // 不计算
s3.forEach(System.out::println); // 计算
创建一个全体自然数的Stream
,不会进行计算,把它转换为上述s2
这个Stream
,也不会进行计算。再把s2
这个无限Stream
转换为s3
这个有限的Stream
,也不会进行计算。只有最后,调用forEach
确实需要Stream
输出的元素时,才进行计算。
Stream流的三类方法:
- 起始方法:获取Stream流
- 中间方法:返回的是Stream流,所以能继续调用Stream流方法,都能支持链式编程
- 终结方法:调用终结方法后就无法使用该Stream流
获取Steam流
- 集合获取Stream流的主要方式是Collection接口中默认方法:
stream()
、parallelStream()
- 数组获取Stream流主要是靠Arrays工具类的
stream()
、Stream类的of()
stream()
/**
* default Stream<E> stream()
* 返回以此集合作为其源的Stream流
*/
Collection<Integer> list = new ArrayList<>();
Stream<Integer> stream = list.stream();
parallelStream()
/**
* default Stream<E> parallelStream()
* 提供了流的并行处理,其底层使用Fork/Join框架实现。
*/
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
numbers.parallelStream().forEach(num ->
System.out.println(Thread.currentThread().getName() + ">>" + num));
- 串行流:适合存在线程安全问题、阻塞任务、重量级任务,以及需要使用同一事务的逻辑。
- 并行流:适合没有线程安全问题、较单纯的数据处理任务。
Map没有不是继承自Collection接口,因此没有这两种方法
Arrays.stream()
Arrays工具类的stream方法
/**
* public static <T> Stream<T> stream(T[] array)
* 返回以指定数组作为其源的Stream流
*/
int[] arr = new int[10];
IntStream stream = Arrays.stream(arr);
Stream.of()
Stream接口的of方法:
/**
* static <T> Stream<T> of(T... values)
* 返回元素为指定值的顺序有序流
*/
Stream<Integer> stream = Stream.of(1, 3, 5, 9, 7, 23);
其底层其实还是调用的Arrays.stream
public static<T> Stream<T> of(T... values) {
return Arrays.stream(values);
}
其他方法
- 传入
Supplier
,调用Stream.generate()
获取Stream流 -
Files
类的lines()
方法:把一个文件变成一个Stream
,每个元素代表文件的一行内容 -
Pattern
类的splitAsStream()
方法:把一个长字符串分割成Stream
序列而不是数组
处理Stream流
方法 | 描述 |
filter(Predicate<? super T> predicate) | 用于对流中的数据进行过滤 |
limit(long maxSize) | 获取前几个元素 |
skip(long n) | 跳过前n个元素 |
distinct() | 去除流中重复的元素。依赖(hashCode和equals方法) |
concat(Stream a, Stream b) | 合并流a和流b。当合并不同类型的流,比如String和Integer,可以使用Object来接收 |
map() | 转换流 |
sorted([Comparator]) | 排序,可选择提供比较器 |
map()
所谓map
操作,就是把一种操作运算,映射到一个序列的每一个元素上。例如,对x
计算它的平方,可以使用函数。我们把这个函数映射到一个序列1,2,3,4,5上,就得到了另一个序列1,4,9,16,25
Stream<Integer> s1 = Stream.of(1, 2, 3, 4, 5);
Stream<Integer> s2 = s1.map(n -> n * n);
利用map()
,不但能完成数学计算,对于字符串操作,以及任何Java对象都是非常有用的。
List.of(" Apple ", " pear ", " ORANGE", " BaNaNa ") // 获取List
.stream() // 转化Stream
.map(String::trim) // 去空格
.map(String::toLowerCase) // 变小写
.forEach(System.out::println); // 打印
工作原理:
map()
方法接收的对象是Function
接口对象,它定义了一个apply()
方法,负责把一个T
类型转换成R
类型
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
Function
的定义是:
@FunctionalInterface
public interface Function<T, R> {
// 将T类型转换为R:
R apply(T t);
int Worlda;
}
filter()
对一个Stream
的所有元素一一进行测试,不满足条件的就被“滤掉”了,剩下的满足条件的元素就构成了一个新的Stream
。如下:
IntStream.of(1, 2, 3, 4, 5, 6, 7, 8, 9)
.filter(n -> n % 2 != 0)
.forEach(System.out::println);
工作原理:
filter()
方法接收的对象是Predicate
接口对象,它定义了一个test()
方法,负责判断元素是否符合条件。
@FunctionalInterface
public interface Predicate<T> {
// 判断元素t是否符合条件:
boolean test(T t);
}
结束Stream流
方法 | 描述 |
void forEach(Consumer<? super T> action) | 遍历,对此流的每个元素执行操作。 |
long count() | 返回此流的元素数 |
终结操作方法,调用完成后流就无法继续使用了,原因是不会返回Stream了。
reduce()
把一个Stream
的所有元素按照聚合函数聚合成一个结果。
// 求和
int sum = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9).reduce(0, (acc, n) -> acc + n);
System.out.println(sum); // 45
// 求积
int s = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9).reduce(1, (acc, n) -> acc * n);
System.out.println(s); // 362880
工作原理:
reduce()
方法传入的对象是BinaryOperator
接口,它定义了一个apply()
方法,负责把上次累加的结果和本次的元素 进行运算,并返回累加的结果。
@FunctionalInterface
public interface BinaryOperator<T> {
// 两个输入,一个输出
T apply(T t, T u); // apply(初始值,循环操作)
}
collect()
把Stream
操作后的结果数据收集到一个集合中。
方法 | 描述 |
collect(Collector collector) | 开始收集Stream流,指定收集器。 |
Collectors类 提供了具体的收集方式:
方法 | 描述 |
Collector toList() | 把元素收集到List集合中 |
Collector toSet() | 把元素收集到Set集合中 |
toMap(Function keyMapper , Function valueMapper) | 把元素收集到Map集合中 |
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
list.add(6);
list.add(3);
list.add(1);
list.add(-2);
list.add(7);
Stream<Integer> stream = list.stream();
List<Integer> objects = stream.sorted().collect(Collectors.toList());
for (Integer object : objects) {
System.out.println(object);
}
}
toArray()
把Stream
操作后的结果数据收集到一个数组中。
方法 | 描述 |
Object[] toArray() | 将流收集到Object类数组 |
| 将流收集到指定类型的数组 |
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
list.add(6);
list.add(3);
list.add(1);
list.add(-2);
list.add(7);
Stream<Integer> stream = list.stream();
Object[] objects = stream.sorted((o1, o2) -> -(o1 - o2)).toArray();
System.out.println(Arrays.toString(objects));
}
标签:Stream,stream,元素,list,add,JDK8,方法 From: https://blog.51cto.com/u_15936519/6050952流只能使用一次,也就是收集过后无法再次收集。