目录
Stream流的作用
结合了Lambda表达式,简化集合、数组的操作
Stream流的使用步骤
-
先得到一条Stream流(流水线),并把数据放上去;
利用Stream流中的API进行各种操作。流的API有中间方法和终结方法,中间方法调用完毕后,还能调用其他的方法;中介方法是最后一步调用的,调用完毕后不能再调用其他方法。
-
使用中间方法对流水线上的数据进行操作;
-
使用终结方法对流水线上的数据进行操作;
获取Steam流
获取方式 | 方法名 | 说明 |
---|---|---|
单列集合(Collection) | default Stream<E> stream() | Collection中的默认方法 |
双列集合(Map) | 无 | 无法直接使用Stream流(先转成单列集合) |
数组 | public static<T> Stream<T> stream(T[] array) | Arrays工具类中的静态方法 |
零散数据 | public static<T> Stream<T> of(T... values) | Stream接口中的静态方法 |
1.单列集合获取Stream流
//导包
import java.util.stream.Stream;
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list, 1, 2, 3, 4, 5, 6, 7);
//获取一条流,并把集合的数据放到流上
//1.用Stream变量接受再打印
Stream<Integer> stream1 = list.stream();
stream1.forEach(i -> System.out.print(i + " "));
//2.通常链式编程直接使用从单列集合获取的Stream流
list.stream().forEach(i -> System.out.print(i + " "));
2.双列集合获取Stream流
第一种方法:使用keySet方法
//1)创建双列集合
HashMap<String, Integer> hm = new HashMap<>();
//2).添加数据
hm.put("aaa", 1);
hm.put("bbb", 2);
hm.put("ccc", 3);
hm.put("ddd", 4);
hm.put("eee", 5);
//3)第一种获取双列集合Stream流的办法
hm.keySet().stream().forEach(k -> System.out.println(k + "=" + hm.get(k)));
第二种方法:使用entrySet方法
hm.entrySet().stream().forEach(entry -> System.out.println(entry.getKey() + "=" + entry.getValue()));
前面创建双列集合的步骤不变
3.数组获取Stream流
//1)创建数组
int[] arr = new int[]{1, 2, 3, 4, 5, 6, 7};
//2)通过类名Arrays调用stream方法,来获取数组的Stream流
Arrays.stream(arr).forEach(i -> System.out.print(i + " "));
不同的是,获取数组的Stream流方法是类 Arrays的静态方法,可以直接通过 类名. 的形式调用。
基本数据类型和引用数据类型的数组 用法一样。
4.一堆零散数据获取Stream流
//调用Stream接口中的静态方法of
Stream.of(1, 2, 3, 4, 5).forEach(i -> System.out.print(i + " "));
基本数据类型和引用数据类型的用法一样。
注:必须是数据类型相同的一堆数据。
注意一个易错点:Stream的of方法接受零散数据的原理是可变参数,所以这个方法也可以获取数组的流。
不过注意 :Stream的of方法的可变形参是泛型T,只能接收引用数据类型,如果传递了基本数据类型的数组作为参数,那么会把整个数组的地址作为一个元素放到Stream中。
String[] strs = {"a", "b", "c"};
Stream.of(strs).forEach(s -> System.out.println(s));//可以正常打印元素a,b,c
//String 是引用数据类型
int[] arr1 = {1, 2, 3};
Stream.of(arr1).forEach(i -> System.out.println(i + " "));//[I@27973e9b
//int 是基本数据类型
Stream流的中间方法
常用API
名称 | 说明 |
---|---|
Stream<T> filter(Predicate<? super T> predicate) | 过滤 |
Stream<T> limit(long maxSize) | 获取前几个元素 |
Stream<T> skip(long n) | 跳过前几个元素,获取后面的 |
Stream<T> distinct() | 元素去重,依赖(hashCode和equals方法) |
static <T> Stream<T> concat(Stream a, Stream b) | 合并a和b两个流为一个流 |
Stream<R> map(Function<T, R> mapper) | 转换流中的数据类型 |
注意点:
-
中间方法,返回新的Stream流,原来的Stream流只能使用一次,建议使用链式编程。
-
修改Stream流中的数据,不会影响原来集合或者数组中的数据。
//创建集合 后面的代码演示都用这个集合
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "a","abs","aa","bs","dfs");
注意点演示
1)
//导包
import java.util.stream.Stream;
Stream<String> stream = list.stream();
Stream<String> stream1 = stream.filter(s -> s.startsWith("a"));
//打印流stream
stream.forEach(s -> System.out.println(s));
//打印流stream1
stream1.forEach(s -> System.out.println(s));
这段代码我先将上面的集合获取到流stream中,然后调用stream的filter过滤方法得到新的流stream1;
再分别打印流stream和stream1。
编译不会报错,运行时得到如下结果:
报错:流已经被操作或关闭;
说明一个流只能使用一次。为了简化代码的书写,也为了避免流的误操作,通常采用链式编程进行书写。
2)
流的使用不会影响原集合或数组中的数据。
方法解析
1)filter
//导包
import java.util.function.Predicate;
.
.
.
list.stream().filter(new Predicate<String>() {
@Override
public boolean test(String s) {
//如果返回值为true,表示当前数据留下
//如果返回值为false,表示当前数据舍弃
return s.startsWith("a");
}
}).forEach(s -> System.out.println(s));
Lambda表达式简化
list.stream().filter(s -> s.startsWith("a")).forEach(s -> System.out.println(s));
2)limit
获取前几个(参数)元素。
list.stream().limit(3).forEach(System.out::println);
3)skip
跳过前几个(参数)元素,获取后面的所有元素。
list.stream().skip(3).forEach(System.out::println);
4)distinct
元素去重,依赖(hashCode和equals方法)。
list.stream().distinct().forEach(System.out::println);
//底层使用 HashSet 去重。
因为distinct底层使用HashSet去重,所以如果是java打包好的类型还好,如果是自定义类型一定要重写hashCode和equals方法。
5)concat
连接两个流
ArrayList<String> list1 = new ArrayList<>();
Collections.addAll(list1,"1","2","3","4");
//5.Stream.concat()
Stream.concat(list1.stream(), list.stream()).forEach(System.out::println);
concat是Stream类里的静态方法(static),使用 类名. 调用。
concat可以连接类型相同的流,获取到的新的流的类型不变;
List<String> list1 = new ArrayList<>();
List<String> list2 = new ArrayList<>();
Stream<String> stream = Stream.concat(list1.stream(), list2.stream());
concat也可以连接类型不同的流,获取到的新的流的类型变成两个类型的父类;
List<String> list1 = new ArrayList<>();
List<Integer> list2 = new ArrayList<>();
Stream<Object> stream = Stream.concat(list1.stream(), list2.stream());
6)map
//6.map
list.clear();
Collections.addAll(list, "a-2","c-4","d-0","b-9");
//使用map方法将流中的整数提取出来
/*
* map中 Function的第一个泛型表示流里面数据的类型,第二个泛型表示要转成的类型
* apply方法
* 形参:依次表示流中的每个数据
* 返回值:转换类型后的数据
* 方法体:要写将一个数据转换数据类型
* */
list.stream().map(new Function<String, Integer>() {
@Override
public Integer apply(String s) {
String[] arr = s.split("-");
return Integer.parseInt(arr[1]);
}
}).forEach(System.out::println);
Lambda表达式简化后
list.stream().map(s -> Integer.parseInt(s.split("-")[1])).forEach(System.out::println);
map方法中函数式接口Function泛型的两个参数
第一个参数是流中原本的数据类型;
第二个类型是转换之后的类型;
接口的方法
apply的形参s:依次表示流里面的每一个元素;
返回值表示转换之后的数据。
当map方法执行完毕之后,留下的数据都是转换之后的结果。
Stream流的终结方法
名称 | 说明 |
---|---|
void forEach(Consumer action) | 遍历 |
long count() | 统计 |
toArray() | 收集流中的数据,放到数组中 |
collect(Collector collector) | 收集流中的数据,放到集合中 |
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list, 1,2,3,4,5);
1)forEach
//1.void forEach(Consumer action) 遍历
//Consumer的泛型,表示流中数据的类型
//accept方法的形参s:以此表示流中的每一个数据
//方法体:对每一个数据的处理操作(打印)
list.stream().forEach(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
System.out.println(integer);
}
});
Lambda表达式简化
list.stream().forEach(i -> System.out.println(i));
2)long count()
统计流中元素个数。
//2.long count() 统计流中元素个数
System.out.println(list.stream().count());
3)toArray
收集元素,转成数组。
空参:直接转成Object类型的数组
//3.toArray() 收集元素,转成数组
Object[] arr1 = list.stream().toArray();
System.out.println(Arrays.toString(arr1));
含参:
//含参
//IntFunction的泛型:具体类型的数组
//apply的形参:流中数据的个数,要跟数组的长度保持一致
//apply的返回值:具体类型的数组
//方法体:创建数组
//toArray方法的参数是创建一个指定长度的数组
//toArray方法的底层,会依次得到流里面的每一个数据,并把数据放到数组当中
//toArray方法的返回值:是一个装着流里面所有数据的数组
Integer[] arr2 = list.stream().toArray(new IntFunction<Integer[]>() {
@Override
public Integer[] apply(int value) {
return new Integer[value];
}
});
System.out.println(Arrays.toString(arr2));
Lambda表达式简化
Integer[] arr3 = list.stream().toArray(value -> new Integer[value]);
或者
Integer[] arr3 = list.stream().toArray(Integer[]::new);
4)collect(Collector collector) 收集流中的数据,放到集合中
4.1)收集到List集合当中
//4.1) 收集到集合当中
ArrayList<String> list1 = new ArrayList<>();
Collections.addAll(list1, "9-aabc","0-uud","4-iwj","7-dads","10-doqw","99-lsks");
List<String> newList = list1.stream()
.filter(s -> Integer.parseInt(s.split("-")[0]) < 10)
.collect(Collectors.toList());
System.out.println("newList" + newList);
收集到List集合中也可以用Stream中的非静态方法toList()
//4.1) 收集到集合当中
ArrayList<String> list1 = new ArrayList<>();
Collections.addAll(list1, "9-aabc","0-uud","4-iwj","7-dads","10-doqw","99-lsks");
List<String> newList = list1.stream()
.filter(s -> Integer.parseInt(s.split("-")[0]) < 10)
.toList();
System.out.println("newList" + newList);
4.2)收集到Set集合当中
//4.2) 收集到Set集合当中
Set<String> newList2 = list1.stream()
.filter(s -> Integer.parseInt(s.split("-")[0]) < 10)
.collect(Collectors.toSet());
System.out.println("newList2" + newList2);
收集到Set集合中可以自动去重
4.3)收集到Map集合当中
//导包
import java.util.stream.Collectors;
//4.3) 收集到map集合当中
Map<Integer, String> map = list1.stream()
.filter(s -> Integer.parseInt(s.split("-")[0]) < 10)
/*
*Collectors.toMap()方法的 第一个参数:表示键的生成规则;
* 第二个参数:表示值的生成规则;
*
* 参数一: Function<String, Object>,第一个泛型表示流中数据的类型,第二个泛型表示Map集合中键的数据类型
* 方法apply的形参:依次表示流里面的每一个数据
* 方法体:生成键
* 返回值:已经生成的键
*
* 参数二: Function<String, Object>,第一个泛型表示流中的数据的类型,第二个泛型表示Map集合中值的数据类型
* 方法apply的形参:依次表示流里面的每一个数据
* 方法体:生成值
* 返回值:已经生成的值
* */
.collect(Collectors.toMap(new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return Integer.parseInt(s.split("-")[0]);
}
}, new Function<String, String>() {
@Override
public String apply(String s) {
return s.split("-")[1];
}
}));
System.out.println("map" + map);
用Lambda表达式简化
System.out.println("----------------------简化后---------------------------");
map = list1.stream()
.filter(s -> Integer.parseInt(s.split("-")[0]) < 10)
.collect(Collectors.toMap( s -> Integer.parseInt(s.split("-")[0]), s -> s.split("-")[1]));
System.out.println("map" + map);
注意:使用toMap方法时,流中的数据的键不能重复,否则会直接报错。