Stream流
1、概述
java.util.stream.Stream接口,表示能应用在一组元素上,一次执行的操作序列,也就是可以对一组数据进行连续的多次操作。
Stream在使用的时候,需要指定一个数据源,比如 java.util.Collection的子类,List或者Set都可以,但是Map类型的集合不支持。
Stream是对集合功能的增强,它提供了各种非常便利、高效的聚合操作,可以大批量数据操作,同时再结合Lambda表达式,就可以极大的提高编程效率。
Stream的API提供了串行和并行两种模式进行操作数据。 Stream操作分为中间操作或者最终操作两种:
- 中间操作:返回Stream本身,这样就可以将多个操作依次串起来(例如map、flatMap、filter、distinct、sorted、peek、limit、skip、parallel、sequential、unordered)
- 最终操作:返回特定类型的计算结果(forEach、forEachOrdered、toArray、reduce、collect、min、max、count、anyMatch、allMatch、noneMatch、findFirst、findAny、iterator)
可以把这个Stream操作理解成穿线,把操作都串起来了
例如:
public class Test {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
Collections.addAll(list,1,2,3,4,5,6,7,8);
list = list.stream()
.filter(e->e%2==0)
.sorted((e1,e2)->e2-e1)
.collect(Collectors.toList());
System.out.println(list);
}
}
分析:
接口Stream<T> filter(Predicate<? super T> predicate); 中的参数是一个Predicate接口,该接口中有个test()抽象方法;接口 Stream<T> sorted(Comparator<? super T> comparator); 中的参数是一个比较器Comparator接口类型的;
可以看出,在list集合转为Stream后,可以经过连续的多次数据操作,最后返回我们想要的结果,并且实现功能代码比之前更加优雅、简洁。你甚至可以把stream操作理解成生产车间的流水线作业,一个最初的产品(数据),经过中间多个连续的不同工序(操作),得到最终的产品。
值、数组、集合、基本类型转Stream
可以将现有的数据,转换为Stream对象,然后再使用Stream的API对数据进行一些系列操作。
值转Stream
通过Arrays.stream()方法将传入的值放到stream对象中,返回stream对象
public Test{
public static void main(String[] args){
Stream<String> stream = Stream.of("a","b","c");
System.out.println(Arrays.toString(stream.toArray()));
}
}
分析:传入泛型类型的值,返回一个stream对象,通过stream.toArray()方法将stream对象转换为数组对象,再通过Arrays工具类的toString方法输出数组。
数组转Stream
两个方法: (1)Arrays工具类中的stream方法可以将数组转换为stream对象 (2)Stream接口中的of方法可以将数组转换为stream对象 例如:
public class Test {
public static void main(String[] args) {
String[] arr = {"a","b","c"};
Stream<String> stream1 = Arrays.stream(arr);
System.out.println(Arrays.toString(stream1.toArray()));
}
}
集合转Stream
public class Test {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"a","b","c");
Stream<String> stream = list.stream();
System.out.println(Arrays.toString(stream.toArray()));
}
}
基本类型转Stream
虽然也可以用Stream类型,并指定泛型: Stream Stream Stream 但是,在数据量较大的时候,自动拆箱/装箱会比较消耗性能,所以提供了下面三种专门针对基本类型的Stream 对于基本数值类型,有专门的三种Stream类型: IntStream LongStream DoubleStream
例如:以IntStream类型为例
public class Test {
public static void main(String[] args) {
IntStream stream1 = IntStream.of(new int[]{1, 2, 3});
IntStream stream2 = IntStream.range(1, 3);
}
}
Stream转数组、集合、字符串等
Stream转数组
public class Test {
public static void main(String[] args) {
Stream<String> stream = Stream.of("hello", "world", "java");
String[] strings = stream.toArray(String[]::new);
System.out.println(Arrays.toString(strings));
}
}
分析,把String[]::new单独拎出来,可以发现它是被IntFunction接收的IntFunction aNew = String[]::new; ,查看IntFunction接口源码可以发现里面有一个抽象方法R apply(int value); ,通过重写该接口中的apply方法,返回一个数组对象,如下:
IntFunction ic = new IntFunction() {
@Override
public Object apply(int value) {
return new String[value];
}
}
简化成lambda表达式如下 IntFunction ic = value->new String[value]; 再次简化就成Strig[] ::new
Stream转集合
public class Test {
public static void main(String[] args) {
Stream<String> stream = Stream.of("hello", "world", "java");
List<String> list = stream.collect(Collectors.toList());
}
}
分析:collect方法中的参数是Collectors接口对象,作用是将元素收集到一个可以修改的容器中,并返回该容器 (1)toList()方法源码如下 主要作用是返回一个List集合,stream.collect()将stream中的内容放到这个List集合中。 (2)toCollection()方法原码如下: ArrayList::new(构造方法引用)可以构造一个ArrayList集合,toCollection()方法将元素放到构造的ArrayList中 (3)toSet()方法和HashSet::new也如上两个这么理解
需要注意的是一个Stream在代码只能使用一次,再次使用就会报错,如多次使用一个Stream对象就会抛出以下异常:java.lang.IllegalStateException: stream has already been operated upon or closed
Stream转字符串
public class Test {
public static void main(String[] args) {
Stream<String> stream = Stream.of("hello", "world", "java");
String result = stream.collect(Collectors.joining("-"));
System.out.println(result);
}
}
Stream中的常见操作
最终操作
(1)iterator()方法
public class Test {
public static void main(String[] args) {
Stream<String> stream = Stream.of("hello", "world", "java");
Iterator<String> it = stream.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
}
(2)forEach()方法 将Stream中的每个元素交给Consumer函数处理
public class Test {
public static void main(String[] args) {
Stream<String> stream = Stream.of("hello", "world", "java");
stream.forEach(System.out::println);
}
}
(3)count()方法 统计流中的元素个数,并返回结果
public class Test {
public static void main(String[] args) {
Stream<String> stream = Stream.of("hello", "world", "java");
System.out.println(stream.count());;
}
}
(4)max()方法 返回流中基于comparator所指定的比较规则,比较出的最大值
public class Test {
public static void main(String[] args) {
Stream<String> stream = Stream.of("hello", "world", "java");
Optional<String> max = stream.max(String::compareTo);
System.out.println(max.get());
}
}
(5)min()方法 返回流中基于comparator所指定的比较规则,比较出的最小值
public class Test {
public static void main(String[] args) {
Stream<String> stream = Stream.of("hello", "world", "java");
Optional<String> max = stream.min(String::compareTo);
System.out.println(max.get());
}
}
(6)collect方法 将元素收集到一个可以修改的容器中,并返回该容器。 使用样例1:
public class Test {
public static void main(String[] args) {
Stream<String> stream = Stream.of("hello", "world", "java","python","javascript","zhangsan");
Map<Integer, List<String>> map = stream.collect(Collectors.groupingBy(String::length));
map.forEach((k,v) -> System.out.println(k + " : " + v));
}
}
4 : [java]
5 : [hello, world]
6 : [python]
8 : [zhangsan]
10 : [javascript]
使用样例2:
public class Test {
public static void main(String[] args) {
Stream<String> stream = Stream.of("hello", "world", "java","python","javascript","zhangsan");
Map<Boolean, List<String>> map = stream.collect(Collectors.partitioningBy(s -> s.indexOf("java") != -1));
map.forEach((k,v) -> System.out.println(k + " : " + v));
}
}
false : [hello, world, python, zhangsan]
true : [java, javascript]
(7)Match()方法 匹配操作,Stream中提供了多种匹配模式
public class Test {
public static void main(String[] args) {
Stream<String> stream = Stream.of("hello", "world", "java","python","javascript","zhangsan");
boolean allMatch = stream.allMatch((s) -> s.startsWith("j"));
System.out.println(allMatch);
}
这些操作不能同时执行,因为一个Stream只能使用一次
(9)findFirst() 返回Stream的第一个元素
public class Test {
public static void main(String[] args) {
Stream<String> stream = Stream.of("hello", "world", "java","python","javascript","zhangsan");
Optional<String> first = stream.findFirst();
System.out.println(first.get());
}
}
中间操作
(1)filter方法() 过滤方法,返回满足predicate指定的条件的所有元素的一个新流
public class Test {
public static void main(String[] args) {
Stream<String> stream = Stream.of("hello", "world", "java","python","javascript","zhangsan");
stream.filter(e -> e.contains("o")).forEach(System.out::println);
}
}
(2)map()方法 对调用流中的元素,应用Function所指定的操作,然后返回一个新流
public class Test {
public static void main(String[] args) {
Stream<String> stream = Stream.of("hello", "world", "java","python","javascript","zhangsan");
List<Integer> list = stream.map(str -> str.length()).collect(Collectors.toList());
list.forEach(System.out::println);
}
}
5
5
4
6
10
8
map生成的是个1:1映射,每个输入元素,都按照规则转换成为另外一个元素
(3)reduce()方法 将一组数据俩俩合并,最后得出一个结果
public class Test {
public static void main(String[] args) {
IntStream stream = IntStream.rangeClosed(1, 10);
int result = stream.reduce(0,(a,b) -> a+b);
System.out.println(result);
}
}
(4)sorted()方法 排序
public class Test {
public static void main(String[] args) {
Stream<String> stream = Stream.of("hello", "world", "java","python","javascript","zhangsan");
stream.sorted().forEach(System.out::println);
}
}
hello
java
javascript
python
world
zhangsan
public class Test {
public static void main(String[] args) {
Stream<String> stream = Stream.of("hello", "world", "java","python","javascript","zhangsan");
stream.sorted((o1,o2) -> -o1.compareTo(o2)).forEach(System.out::println);
}
}
(5)limit()方法 返回Stream 的前面 n 个元素
public class Test {
public static void main(String[] args) {
Stream<String> stream = Stream.of("hello", "world", "java","python","javascript","zhangsan");
stream.limit(5).forEach(System.out::println);
}
}
(6)skip()方法 跳过前 n 个元素只要后面的元素。
public class Test {
public static void main(String[] args) {
Stream<String> stream = Stream.of("hello", "world", "java","python","javascript","zhangsan");
stream.skip(3).forEach(System.out::println);
}
}
(7)distinct()方法 去除重复数据
public class Test {
public static void main(String[] args) {
Stream<String> stream = Stream.of("hello","hello", "world", "java","python","javascript","zhangsan");
stream.distinct().forEach(System.out::println);
}
}
静态方法
(1)concat()方法 拼接两个流
public class Test {
public static void main(String[] args) {
Stream<String> stream1 = Stream.of("hello", "world");
Stream<String> stream2 = Stream.of("tom", "mary");
Stream<String> concat = Stream.concat(stream1, stream2);
concat.forEach(System.out::println);
}
}
(2)Stream.generate()方法 通过Supplier接口,可以自己来控制数据的生成。
public class Test {
public static void main(String[] args) {
Random random = new Random();
Stream.generate(() -> random.nextInt(100))
.limit(100)
.forEach(System.out::println);
Stream.generate(()->random.nextInt())
.limit(100)
.collect(Collectors.toList());
}
}
Stream.iterate,它跟 reduce 操作很像,需要接受一个起始值(种子),然后通过函数得出一个结果,再把结果当做参数传给函数,再得出第二个结果,依次类推,其实就是一个递归操作。
IO流与Stream
在IO流中,也有方法,可以将读取到的数据转换为Stream对象。 例如:
public class Test {
public static void main(String[] args) throws FileNotFoundException {
BufferedReader br = null;
br = new BufferedReader(new FileReader("src/com/demo.a.txt"));
int maxLen = br.lines()
.mapToInt(String::length)
.max()
.getAsInt();
System.out.println(maxLen);
try {
br.close();
} catch (IOException exception) {
exception.printStackTrace();
}
}
}
并行流
Stream有串行和并行两种。串行Stream上的操作是在一个线程中依次完成,而并行Stream则是在多个线程上同时执行。
创建并行Stream的两种方式: (1)调用串行Stream的parallel()方法,可以将其转换并行Stream
Stream<String> stream = Stream.of("hello","world","java");
Stream<String> parallelStream = stream.parallel();
(2)调用集合对象的parallelStream方法,之后获取并行Stream
List<String> list = new ArrayLIst<>();
Collections.addAll(list,"hello","world","java");
Stream<String> parallelStream = list.parallelStream();
|