Stream流
1.Sream流概述
我们可以把集合|数组,转换为Stream流,使用Stream流中的方法,对集合|数组进行操作
2.流式思想概述
3.获取Stream流的方式
使用Collection接口中,(jdk1.8之后)定义了一个方法stream,可以把集合转换为Stream流
default Stream<E> stream() // 此转换方法只能是Collection接口下的集合使用
使用Stream接口中方法of,可以把可变参数(数组)转换为Stream流
java.util.stream.Stream 接口(jdk1.8之后)
static <T> Stream<T> of(T... values) // 方法的参数是一个可变参数,也可以传递数组
import java.util.*;
import java.util.stream.Stream;
public class Demo03Stream {
public static void main(String[] args) {
show02();
}
/*
把数组转换为Stream流
注意:
看到的方法参数是一个可变参数,那么就可以传递数组,因为可变参数底层就是一个数组
static <T> Stream<T> of (T... values) :方法的参数是一个可变参数,也可以传递数组
*/
private static void show02() {
Stream<Integer> stream1 = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9);
Stream<String> stream2 = Stream.of("a", "b", "c");
String[] arr1 = {"a", "b", "c"};
Stream<String> stream3 = Stream.of(arr1);
//注意:数组的数组类型必须是包装类,不要使用基本数据类型
int[] arr2 = {1,2,3};
Stream<int[]> stream4 = Stream.of(arr2);
System.out.println(stream4.count());//1 把数组作为一个元素,转换为Stream流
Integer[] arr3 = {1,2,3};
Stream<Integer> stream5 = Stream.of(arr3);
System.out.println(stream5.count());//3
}
/*
把集合转换为Stream流
*/
private static void show01() {
ArrayList<Integer> list = new ArrayList<>();
Stream<Integer> stream1 = list.stream();
LinkedList<String> linked = new LinkedList<>();
Stream<String> stream2 = linked.stream();
HashSet<Integer> set = new HashSet<>();
Stream<Integer> stream3 = set.stream();
HashMap<String,String> map = new HashMap<>();
//map.stream();//Cannot resolve method 'stream()' Map集合不能直接转换为Stream流
//获取Map集合中所有的key
Set<String> keySet = map.keySet();
Stream<String> stream4 = keySet.stream();
//获取Map集合中所有的value
Collection<String> values = map.values();
Stream<String> stream5 = values.stream();
//获取Map集合中所有的Entry对象
Set<Map.Entry<String, String>> entrySet = map.entrySet();
Stream<Map.Entry<String, String>> stream6 = entrySet.stream();
}
}
4.Stream的常用方法
流模型的操作很丰富,这里介绍一些常用的API。这些方法可以被分成两种:
- 终结方法:返回值类型不再是
Stream
接口自身类型的方法,因此不再支持类似StringBuilder
那样的链式调用。本小节中,终结方法包括count
和forEach
方法。 - 非终结方法:返回值类型仍然是
Stream
接口自身类型的方法,因此支持链式调用。(除了终结方法外,其余方法均为非终结方法。)
函数拼接与终结方法
在上述介绍的各种方法中,凡是返回值仍然为Stream
接口的为函数拼接方法,它们支持链式调用;而返回值不再为Stream
接口的为终结方法,不再支持链式调用。如下表所示:
方法名 | 方法作用 | 方法种类 | 是否支持链式调用 |
---|---|---|---|
filter | 过滤 | 函数拼接 | 是 |
limit | 取用前几个 | 函数拼接 | 是 |
skip | 跳过前几个 | 函数拼接 | 是 |
map | 映射 | 函数拼接 | 是 |
concat | 组合 | 函数拼接 | 是 |
count | 统计个数 | 终结 | 否 |
forEach | 逐一处理 | 终结 | 否 |
备注:本小节之外的更多方法,请自行参考API文档。
forEach 方法:用于遍历
void forEach(Consumer<? super T> action) // 对此流的每个元素执行操作。
// 参数:
// Consumer<? super T> action:是一个消费型的接口,参数可以传递Lambda表达式
// 唯一的抽象方法:void accept(T t) 消费一个指定泛型类型的数据
注意:
forEach方法是一个终结方法,没有返回值;也不能使用链式编程调用Stream流中的其他方法了
import java.util.stream.Stream;
public class Demo04forEach {
public static void main(String[] args) {
//获取Stream流
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
//使用forEach方法遍历Stream流中的元素
/*stream.forEach((Integer i)->{
System.out.println(i);
});*/
/*
IllegalStateException: stream has already been operated upon or closed
Stream流对象只能使用一次,使用完毕就会流向下一个对象
之前的Stream流对象就已经关闭了,被销毁了,所以就不能在使用了,会抛出非法状态异常
简化lambda表达式
*/
stream.forEach(i-> System.out.println(i));
}
}
count 方法:统计个数
long count() // 返回此流中的元素数。
注意:count方法是一个终结方法,返回值类型是long类型;也不能使用链式编程调用Stream流中的其他方法了
import java.util.ArrayList;
import java.util.Collections;
import java.util.stream.Stream;
public class Demo05count {
public static void main(String[] args) {
//获取Stream流
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
long c1 = stream.count();
System.out.println(c1);//10
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"aa","bb","cc","dd","ee");
//把集合转换为Stream流
Stream<String> stream2 = list.stream();
System.out.println(stream2.count());//5
}
}
filter 方法:过滤元素
Stream<T> filter(Predicate<? super T> predicate)
// 参数:
// Predicate<? super T> predicate:函数式接口,参数传递lambda表达式
// 唯一的抽象方法:boolean test(T t) 用于对接口指定泛型类型的数据进行判断
注意:filter方法的返回值类型还是Stream类型,是一个非终结方法,可以使用返回的Stream对象继续调用Stream流中的方法(链式编程)
import java.util.stream.Stream;
public class Demo06filter {
public static void main(String[] args) {
//创建一个Stream流
Stream<String> stream = Stream.of("美羊羊", "喜羊羊", "懒羊羊", "沸羊羊", "暖羊羊", "慢羊羊", "灰太狼", "红太狼", "小灰灰");
//使用filter方法过滤Stream流中的元素,只要包含羊羊的
//Stream<String> stream2 = stream.filter((String s) -> {
// return s.contains("羊羊");
//});
//遍历stream2流对象
//stream2.forEach(s-> System.out.println(s));
//链式编程
stream.filter(s->s.contains("羊羊")).forEach(s-> System.out.println(s));
}
}
limit 方法:获取前n个元素
Stream<T> limit(long maxSize) // 返回由此流的元素组成的流,截短长度不能超过 maxSize 。
// 例如:limit(4);获取流中的前4个元素,把4个元素存储到一个新的Steam流中
注意:
1.传递数字大于流的长度,返回流中所有的元素
2.limit方法的返回值类型还是Stream类型,是一个非终结方法,可以使用返回的Stream对象继续调用Stream流中的方法(链式编程)
import java.util.stream.Stream;
public class Demo07limit {
public static void main(String[] args) {
//创建一个Stream流
Stream<String> stream = Stream.of("美羊羊", "喜羊羊", "懒羊羊", "沸羊羊", "暖羊羊", "慢羊羊", "灰太狼", "红太狼", "小灰灰");
//获取Stream流中的前4个元素,存储到一个新的Stream流中返回
//Stream<String> stream2 = stream.limit(4);
//stream2.forEach(s-> System.out.println(s));
//stream.limit(4).forEach(s-> System.out.println(s));
stream.limit(10).forEach(s-> System.out.println(s));//9个
}
}
skip 方法:跳过前n个元素
Stream<T> skip(long n)
注意:
1.skip跳过的元素数量大于流中的个数,返回一个没有元素的空流
2.skip方法的返回值类型还是Stream类型,是一个非终结方法,可以使用返回的Stream对象继续调用Stream流中的方法(链式编程)
import java.util.stream.Stream;
public class Demo08skip {
public static void main(String[] args) {
//获取Stream流
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
//跳过前6个元素,把剩余的元素,存储到一个新的Stream流中返回
//Stream<Integer> stream2 = stream.skip(6);
//stream2.forEach(s-> System.out.println(s));
//stream.skip(6).forEach(s-> System.out.println(s));
//stream.skip(11).forEach(s-> System.out.println(s));
System.out.println(stream.skip(11).count());//0 skip跳过的元素数量大于流中的个数,返回一个没有元素的空流
}
}
map 方法:映射,类型转换
<R> Stream<R> map(Function<T,R> mapper)
// 参数:
// Function<T,R> mapper:函数式接口,可以传递Lambda表达式
// 接口中唯一的抽象方法:R apply(T t) 根据参数类型T获取类型R类型的返回值,用于类型转换 T转换R
注意:
map方法的返回值类型还是Stream类型,是一个非终结方法,可以使用返回的Stream对象继续调用Stream流中的方法(链式编程)
import java.util.stream.Stream;
public class Demo09map {
public static void main(String[] args) {
Stream<String> stream = Stream.of("11", "22", "33", "44");
//使用map方法,把字符串类型的Stream流,转换为Integer类型的Stream流返回
//Stream<Integer> stream2 = stream.map((String s) -> {
// return Integer.parseInt(s);
//});
//遍历stream2流对象
//stream2.forEach(s-> System.out.println(s+10));
stream.map(s->Integer.parseInt(s)).forEach(s-> System.out.println(s));
Stream<String> stream3 = Stream.of("迪丽热巴", "杨幂", "柳岩");
//使用map方法,把字符串类型的Stream流,转换为Person类型的流返回
//Stream<Person> stream4 = stream3.map(s -> new Person(s));
//stream4.forEach(s-> System.out.println(s));
stream3.map(s->new Person(s)).forEach(p-> System.out.println(p));
}
}
concat 方法:组合
// 把两个Stream流,组合为一个新的Stream流
static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
注意:
concat方法的返回值类型还是Stream类型,是一个非终结方法,可以使用返回的Stream对象继续调用Stream流中的方法(链式编程)
import java.io.Serializable;
import java.util.stream.Stream;
public class Demo10concat {
public static void main(String[] args) {
Stream<String> stream1 = Stream.of("美羊羊", "喜羊羊", "懒羊羊", "沸羊羊", "暖羊羊", "慢羊羊", "灰太狼", "红太狼", "小灰灰");
Stream<String> stream2 = Stream.of("迪丽热巴", "杨幂", "柳岩");
//使用concat方法,把stream1和stream2组合为一个新的流
//Stream<String> concat = Stream.concat(stream1, stream2);
//遍历Stream流
//concat.forEach(s-> System.out.println(s));
Stream.concat(stream1, stream2).forEach(s-> System.out.println(s));
Stream<Integer> stream3 = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Stream<String> stream4 = Stream.of("11", "22", "33", "44");
Stream<Object> stream5 = Stream.concat(stream3, stream4);
stream5.forEach(s-> System.out.println(s));
}
}
sorted 方法:排序
Stream<T> sorted(Comparator<? super T> comparator)
flatMap 方法:维度升降
参考:https://blog.csdn.net/dengjili/article/details/90557392
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
可以将 Stream<Stream> 升维成 Stream,从而实现将 List<List> 里的对象集合快速合并为 List
@Test
public void test(){
Map<String, ArrayList<TestVo>> map = Maps.newHashMap("1",
Lists.newArrayList(TestVo.builder().data("3").build(), TestVo.builder().data("1").build()));
map.put("0", Lists.newArrayList(TestVo.builder().data("8").build()));
map.put("3", Lists.newArrayList(TestVo.builder().data("2").build()));
List<TestBaseVo> list = map.entrySet().stream()
.map(s -> s.getValue().stream()
.sorted(Comparator.comparing(TestVo::getData))
.map(t -> TestBaseVo.builder().clazz(t.getData()).build()))
.flatMap(a -> a)
.collect(Collectors.toList());
System.out.println(list);
}
5.收集Stream结果(重点)
把Stream流转换为集合或者把Stream流转换为数组
1).把Stream流转换为集合
使用Stream流中的方法:
R collect(Collector<?> collector) //把Stream流转换为集合
// 参数:Collector是一个接口,需要传递接口的实现类对象
java.util.stream.Collectors:是一个工具类,里边提供的静态的方法,可以获取Collector接口的实现类对象
static <T> Collector<T, ?, List<T>> toList() // 返回一个 Collector ,将输入元素累加到一个新的 List
static <T> Collector<T, ?, Set<T>> toSet() // 返回一个 Collector ,将输入元素累加到一个新的 Set
public static <T, K, U> Collector<T, ?, Map<K,U>> toMap(
Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper)
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Demo01StreamToCollection {
public static void main(String[] args) {
//创建一个Stream流
Stream<String> stream = Stream.of("美羊羊", "喜羊羊",
"懒羊羊", "沸羊羊", "暖羊羊", "慢羊羊", "灰太狼", "红太狼", "小灰灰", "灰太狼");
//把Stream流转换为List集合(有序,有索引,允许重复)
//List<String> list = stream.collect(Collectors.toList());
//System.out.println(list);//[美羊羊, 喜羊羊, 懒羊羊, 沸羊羊, 暖羊羊, 慢羊羊, 灰太狼, 红太狼, 小灰灰, 灰太狼]
//把Stream流转换为Set集合(不包含带索引的方法,不允许存储重复的元素)
Set<String> set = stream.collect(Collectors.toSet());
System.out.println(set);//[美羊羊, 沸羊羊, 红太狼, 灰太狼, 暖羊羊, 小灰灰, 喜羊羊, 懒羊羊, 慢羊羊]
}
}
2).把Stream流转换为数组
Stream流中的方法:
Object[] toArray() // 返回一个包含此流的元素的数组。
import java.util.stream.Stream;
public class Demo02StreamToArray {
public static void main(String[] args) {
//创建一个Stream流
Stream<String> stream = Stream.of("美羊羊", "喜羊羊",
"懒羊羊", "沸羊羊", "暖羊羊", "慢羊羊", "灰太狼", "红太狼", "小灰灰", "灰太狼");
Object[] arr = stream.toArray();
for (Object o : arr) {
System.out.println(o);
}
}
}