Java – 集合 – Stream流
Stream流的思想
Stream流在Java中意指是流水线的意思,流水线就是一个产品在一条线上分开各个步骤进行加工,在Java意指可以把一个集合像流水线一样以链式调用的方式对这个集合进行各种方法的加工。Stream流可以直接应用在 List,Set集合中,而Map集合因为属于双列集合不能直接使用Stream流,但可以通过取出keySet() 或 entrySet() 的方式转化为 Set 集合后,再进行流操作。
List集合Stream流
List集合可以调用对象方法获得Stream流对象
List<String> list = new ArrayList<>();
Collections.addAll(list,"aaa","bbb","ccc");
// 直接通过对象获得 Stream流 对象
Stream<String> listStream = list.stream();
Set集合Stream流
Set 集合可以调用对象方法获得Stream流对象
Set<Integer> set = new HashSet<>();
Collections.addAll(set,1,2,3,4,5,6);
Stream<Integer> setStream = set.stream();
数组Stream流
数组不能直接使用对象方法获取,但在 Arrays 数组工具类中,提供了静态方法,可获取数组中的 Stream流 对象
int[] ints = {9, 8, 7, 6, 5, 4, 3, 2, 1};
IntStream intStream = Arrays.stream(ints);
Map集合Stream流
Map 集合因为是双列集合的原因,具有keyValue键值对,不能直接使用 Stream流,Stream流 只能用在单列集合中,那如果想利用 Stream流 对 Map集合 进行流水线加工操作的话,可以先把 Map集合取出 Set集合 ,再进行操作,可转换有 keySet, entrySet
HashMap<String, Integer> map = new HashMap<>();
map.put("aaa", 111);
map.put("bbb", 222);
map.put("ccc", 333);
map.put("ddd", 444);
// 通过取出 keySet 再进行获取 Stream流
Stream<String> mapKeySetStream = map.keySet().stream();
// 通过取出 entrySet 再进行获取 Stream流
Stream<Map.Entry<String, Integer>> mapEntrySetStream = map.entrySet().stream();
零散数据Stream流
如果有多个相同引用类型的数据或一个数组,没有进行List或Set等包装单列的方法时,要获取零散数据的Stream流,可以使用 Stream 类的静态方法 Stream.of()
Stream<String> otherStream = Stream.of("fff", "ggg", "hhh", "jjj");
注意: Stream.of() 方法接收的是一个可变参数,可变参数意味着,它可以直接接收一个数组,但这个数组必须为引用类型的,因为当传入一个基本数据类型的数组的时候,Stream.of() 会把这个数组整体当作是一个引用类型,因此,当数据为基本数据类型的数组时,应该使用 Arrays.stream() 方法进行获取。
// 创建字符串数组
String[] strings = {"aaa", "bbb", "ccc", "ddd"};
// 因为字符串本身是引用类型,所以可以直接使用 of() 方法
Stream.of(strings);
// 创建整型数组
int[] ints = {111,222,333,444};
// 因为整型是基本数据类型,直接传入 of() 方法中,会把整个整型数组当作一个引用类型
Stream.of(ints); // => 输出内存地址 [I@e397777
中间方法
所有能使用Stream流的对象,都可以使用下面常用的加工方法。
注意:
1.中间方法,返回新的Stream流,原来的Stream流只能使用一次,建议使用链式编程
2.修改Stream流中的数据,不会影响原来集合或者数组中的数据
filter过滤
// 过滤方法,使用匿名内部类或Lambda方法,返回boolean,当返回
true时,则留下,返回 false 时则过滤掉
Stream<T> filter(Predicate<? super T> predicate)
limit 取前几位
// 获取前几个元素,如果为3,则取前面3个元素
Stream<T> limit(long maxSize)
skip 跳过前几位
// 跳过前几个元素,如果为3,则跳过前面3个元素,取后面的元素
Stream<T> skip(long n)
distinct 去重
// 元素去重,依赖 hashCode 和 equals 方法,如果是自定义类,需要重写这两个方法
Stream<T> distinct()
concat 合并
// 合并a和b两个流为一个流,请尽量合并相同类型的流,合并出来的流类型为它们俩的共同父类
static <T> Stream<T> concat(Stream a, Stream b)
// 示例;
ArrayList<String> number = new ArrayList<>();
Collections.addAll(number, "111", "222", "333");
// 合并两个流
Stream.concat(number.stream(),list.stream());
map类型转换
// 转换流中类型,比如把流中的String类型转为int类型,需要传入一个匿名内部类,或Lambda自定义方法进行操作处理,并返回
Stream<R> map(Function<T, R> mapper)
// 示例:
ArrayList<String> number = new ArrayList<>();
Collections.addAll(number, "111", "222", "333");
// 使用匿名内部类方式
number.stream().map(new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return Integer.parseInt(s);
}
});
// 使用 Lambda 方式
number.stream().map(s->Integer.parseInt(s)).forEach(s-> System.out.println(s));
终结方法
终结方法指的是Stream流中加工完成后的最后方法,调用终结方法之后,Stream流将被销毁关闭。
forEach遍历
// 遍历得出所有数据出来
void forEach(Consumer action)
count 统计
// 统计得出数量
long count()
toArray 收集到数组中
// 收集数据到数组中
toArray()
toArray 方法有三个,分别如下
// 以 Object 数组的形式返回
public Object[] toArray()
// 创建一个对应类型的数组,以对应类型返回
public <T> T[] toArray(T[] a)
// 以接口实现的方式传入一个对应类型的数组,以对应类型返回
default <T> T[] toArray(IntFunction<T[]> generator)
关于第三种实现接口的方法使用
number.toArray(new IntFunction<String[]>() {
@Override
public String[] apply(int value) {
return new String[value];
}
});
需要实现 IntFunction 接口的方法 apply(int value),
其中的 apply 的形参 value 会接收这个 Stream流 加工完后的成员数,
返回需要提供一个对应类型的数组即可,Java会在底层收到这个对应类型的数组进行数据填充,并返回。
因此,可以在创建对应类型的数组时,指定数组成员数为 value 个就可以了。
collect 收集数据
collect 相对比较复杂,分几种情况进行收集
// 收集数据到集合中
collect(Collector collector)
收集到List集合中
使用 Collectors.toList() 方法,可以把加工后的数据转为 List集合
List<String> collect = list.stream().collect(Collectors.toList());
收集到Set集合中
使用 Collectors.toSet() 方法,可以把加工后的数据转为 Set 集合
Set<String> collect = set.stream().collect(Collectors.toSet());
收集到Map集合中
Map转出就比较复杂了,因为Map是双列集合,包含了key和value值,而stream只输出单列集合,因此需要分别实现输出key的值,和value的值
使用 Collectors.toMap() 方法,并传入两个接口实现匿名内部类(或Lambda方法)
语法:
collect(Collectors.toMap(new Function<Stream流当前类型, Map集合中的key类型>() key , new Function<Stream流当前类型, Map集合中的value类型>() value))
匿名内部类
Map<String, Integer> collect2 = number.stream().collect(Collectors.toMap(
// 实现接口方法,提供 Map 集合中的 key 值
new Function<String, String>() {
@Override
public String apply(String s) {
return null;
}
// 实现接口方法,提供 Map 集合中的 value 值
}, new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return null;
}
}));
关于实现方法 new Function<String, String> () key 的说明
Function 接口定义了两个泛型,其中第一个表示现在的 stream流 中的数据类型,而第二个则表示,你将存到 Map 集合中的 key 值的类型。
new Function<String, Integer> () value 中,第一个表示现在的 stream流 中的数据类型,而第二个则珠示,你将存到 Map 集合中的 value 值的类型
ArrayList<String> names = new ArrayList<>();
Collections.addAll(names, "小明-20", "小红-15", "小刚-30", "小黑-10");
Map<String, Integer> collect2 = names.stream().collect(Collectors.toMap(
new Function<String, String>() {
@Override
public String apply(String s) {
// 这里会接收到 Stream流 中的每一个成员
// 第一个为生成 key 的值
// 使用分割文本的方式,把【小明-20】中的【小明】和【20】分离出来,并取第一个【小明】
return s.split("-")[0];
}
}, new Function<String, Integer>() {
@Override
public Integer apply(String s) {
// 这里是要求提供 Map 集合的 Value 值
// 使用分割文本的方式,把【小明-20】中的【小明】和【20】分离出来,并取第二个【20】
// 但是分离出来之后,它依然是个文本,而我们这里定义了返回是 Integer 类型,所以要转换
return Integer.parseInt(s.split("-")[1]);
}
}));
Lambda语法
names.stream().collect(Collectors.toMap(s->s.split("-")[0],s->Integer.parseInt(s.split("-")[1])));
注意:
如果我们要收集到Map集合当中,应确保键不能重复,否则会报异常
共有 0 条评论