java.util.stream.Stream接口,表示能应用在一组元素上,一次执行的操作序列,也就是可以对一组数据进行连续的多次操作。
Stream在使用的时候,需要指定一个数据源,比如 java.util.Collection的子类,List或者Set都可以,但是Map类型的集合不支持。
Stream是对集合功能的增强,它提供了各种非常便利、高效的聚合操作,可以大批量数据操作,同时再结合Lambda表达式,就可以极大的提高编程效率。
Stream的API提供了串行和并行两种模式进行操作数据。
Stream操作分为中间操作或者最终操作两种:
可以把这个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()); //最终操作,收到一个新的List集合中并返会
System.out.println(list); //[8, 6, 4, 2]
}
}
分析:
接口
Stream
中的参数是一个Predicate接口,该接口中有个test()抽象方法;接口filter(Predicate super T> predicate); Stream
中的参数是一个比较器Comparator接口类型的;sorted(Comparator super T> comparator);
可以看出,在list集合转为Stream后,可以经过连续的多次数据操作,最后返回我们想要的结果,并且实现功能代码比之前更加优雅、简洁。你甚至可以把stream操作理解成生产车间的流水线作业,一个最初的产品(数据),经过中间多个连续的不同工序(操作),得到最终的产品。
可以将现有的数据,转换为Stream对象,然后再使用Stream的API对数据进行一些系列操作。
通过Arrays.stream()方法将传入的值放到stream对象中,返回stream对象
public Test{
public static void main(String[] args){
Stream<String> stream = Stream.of("a","b","c"); //将值转stream
System.out.println(Arrays.toString(stream.toArray()));
}
}
分析:传入泛型类型的值,返回一个stream对象,通过stream.toArray()方法将stream对象转换为数组对象,再通过Arrays工具类的toString方法输出数组。
两个方法:
(1)Arrays工具类中的stream方法可以将数组转换为stream对象
(2)Stream接口中的of方法可以将数组转换为stream对象
例如:
public class Test {
public static void main(String[] args) {
String[] arr = {"a","b","c"};
//第一种方式Arrays.stream(数组)
Stream<String> stream1 = Arrays.stream(arr);
//第二种方式:Stream.of(数组)
//Stream stream2 = Stream.of(arr);
//测试
System.out.println(Arrays.toString(stream1.toArray())); //[a, b, c]
//System.out.println(Arrays.toString(stream2.toArray())); //[a, b, c]
}
}
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(); //将集合list转换为stream对象
System.out.println(Arrays.toString(stream.toArray())); //[a,b,c]
}
}
虽然也可以用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); //表示1到3的范围内为左闭右开[1,3)
//IntStream stream3 = IntStream.rangeClosed(1, 3); //左右都闭[1,3]
}
}
public class Test {
public static void main(String[] args) {
Stream<String> stream = Stream.of("hello", "world", "java"); //将三个字符串转换为stream
String[] strings = stream.toArray(String[]::new); //将stream转换为数组
//String[]::new是构造器引用中的数组构造引用,且是String类型的数组,通过该方式创建一个数组对象,将stream转换过来的值存放到该数组对象中
System.out.println(Arrays.toString(strings)); //[hello, world, java]
}
}
分析,把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
public class Test {
public static void main(String[] args) {
Stream<String> stream = Stream.of("hello", "world", "java"); //将三个字符串转换为stream
List<String> list = stream.collect(Collectors.toList());
// ArrayList list = stream.collect(Collectors.toCollection(ArrayList::new));
// Set set = stream.collect(Collectors.toSet());
// HashSet set = stream.collect(Collectors.toCollection(HashSet::new));
}
}
分析: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
public class Test {
public static void main(String[] args) {
Stream<String> stream = Stream.of("hello", "world", "java");
String result = stream.collect(Collectors.joining("-")); //使用“-”将字符串连接起来hello-world-java
System.out.println(result);
}
}
(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 max = stream.max(new Comparator(){
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
});
*/
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 min = stream.min(new Comparator(){
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
});
*/
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");
//把Stream中的元素,按照字符串长度进行分组,长度相同算是一组,并存放到同一个集合中
//map的key是字符串的长度,value是同一组的数据
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");
//把Stream中的元素,按照指定条件分割成俩组,条件返回true是一组,条件返回false是另一组
//map的key是true或者false,value是对应的数据
// 按照数据中是否包含"java"字符串来进行划分
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");
//所有元素匹配成功才返回true 否则返回false
boolean allMatch = stream.allMatch((s) -> s.startsWith("j"));
System.out.println(allMatch);
//任意一个匹配成功就返回true 否则返回false
// boolean anyMatch = stream.anyMatch((s)->s.startsWith("j"));
// System.out.println(anyMatch);
//没有一个匹配的就返回true,否则返回false
// boolean noneMatch = stream.noneMatch((s) -> s.startsWith("j"));
// System.out.println(noneMatch);
}
这些操作不能同时执行,因为一个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()); //hello
}
}
(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) {
// Stream stream = Stream.of("hello", "world", "java","python","javascript","zhangsan");
IntStream stream = IntStream.rangeClosed(1, 10);
//reduce方法需要提供一个起始值(种子)
// 然后依照运算规则,和Stream中的第一个数据进行操作,得出结果
// 再将这个结果和Stream中的第二个数据进行操作,再得出结果,依次类推,直到得出最终结果
int result = stream.reduce(0,(a,b) -> a+b);
System.out.println(result); //55
}
}
(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");
//比较器排序,注意Lambda表达式中返回的值前加了符号
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); //跳过前面3个元素
}
}
(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();
//生成100个随机数,并输出
Stream.generate(() -> random.nextInt(100))
.limit(100)
.forEach(System.out::println);
//生成100个随机数,并存放到集合中
Stream.generate(()->random.nextInt())
.limit(100)
.collect(Collectors.toList());
}
}
Stream.iterate,它跟 reduce 操作很像,需要接受一个起始值(种子),然后通过函数得出一个结果,再把结果当做参数传给函数,再得出第二个结果,依次类推,其实就是一个递归操作。
在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() //io流转为Stream
.mapToInt(String::length) //每行字符串转换为它的字符长度
.max() //获取最大长度的数字
.getAsInt(); //返回int类型的数据
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();