1.StreamAPI可以做什么
StreamAPI为我们提供了强大的集合操作,同时StreamAPI操作简单,代码直观,容易上手。
2.Stream的操作步骤
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
1.创建Stream
Stream<Integer> stream = integers.stream();
2.中间操作
注意:没有终止操作是不会执行中间操作的
Stream<Integer> integerStream = stream.filter(num -> {
System.out.println("current num is " + num);
return num > 2;
});
3.终止操作
List<Integer> collect = integerStream.collect(Collectors.toList());
System.out.println(collect);//[3, 4, 5, 6, 7]
综合成一条语句就是
List<Integer> collect = integers.stream().filter(num -> {
System.out.println("current num is " + num);
return num > 2;
}).collect(Collectors.toList());
3.常见的中间操作
3.1筛选和切片
- filter:过滤出某些元素
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
//filter(num -> num > 2),此处是过滤出大于2的元素
List<Integer> collect = integers.stream().filter(num -> num > 2).collect(Collectors.toList());
System.out.println(collect);//[3, 4, 5, 6, 7]
- limit:过滤出某些元素
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
//limit(3),获取过滤出的前3个元素
List<Integer> collect = integers.stream().filter(num -> num > 2).limit(3).collect(Collectors.toList());
System.out.println(collect);//[3, 4, 5]
- skip:跳过多少个元素
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
//skip(2).limit(3),此处是先跳过2个元素再剩余流中的3个元素
List<Integer> collect = integers.stream().filter(num -> num > 2).skip(2).limit(3).collect(Collectors.toList());//[5, 6, 7]
//limit(3).skip(2),此处是获取流中的前面3个元素,再跳过两个
List<Integer> collect = integers.stream().filter(num -> num > 2).limit(3).skip(2).collect(Collectors.toList());//[5]
- distinct:去除重复
List<Integer> integers = Arrays.asList(1, 3, 3, 3, 7);
List<Integer> collect = integers.stream().distinct().collect(Collectors.toList());
System.out.println(collect);//[1, 3, 7]
3.2映射
- map:将元素转映射成其他形式的元素
List<Integer> integers = Arrays.asList(1, 2, 3);
List<Integer> collect = integers.stream().map(num -> num * 2).collect(Collectors.toList());//[2, 4, 6]
//此处将num映射成了String类,当然也可以映射成其他类
List<String> collect = integers.stream().map(num -> "I am "+num).collect(Collectors.toList());//[I am 1, I am 2, I am 3]
- flatMap:将元素打平
String[] strings = {"Hello", "World"};
//[ [H,e,l,l,o] , [W,o,r,l,d] ]
List<String[]> collect = Arrays.stream(strings).map(s -> s.split("")).collect(Collectors.toList());
可以发现,我们通过map映射之后一个数组里面还有两个数组[ [H,e,l,l,o] , [W,o,r,l,d] ]
这样的形式,我们可能需要的是[H, e, l, l, o, W, o, r, l, d]
这样形式,那么怎么办呢,这时候就需要用到flatMap
//flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
//Arrays.stream(T[] array)
//flatMap的返回值是一个流所以这里使用Arrays.stream(),这里可以使用静态引用的方式Arrays::stream
//List<String> resultCollect = collect.stream().flatMap(Arrays::stream).collect(Collectors.toList());
List<String> resultCollect = collect.stream().flatMap(strs -> Arrays.stream(strs)).collect(Collectors.toList());
System.out.println(resultCollect);//[H, e, l, l, o, W, o, r, l, d]
3.3排序
- sorted():增序排序
List<Integer> integers = Arrays.asList(1, 9, 3, 77, 52, 7);
List<Integer> collect = integers.stream().sorted().collect(Collectors.toList());
System.out.println(collect);
- sorted(Comparator<? super T> comparator):自定义排序
//降序排序
List<Integer> integers = Arrays.asList(1, 9, 3, 77, 52, 7);
List<Integer> collect = integers.stream().sorted((n1, n2) -> n2 - n1).collect(Collectors.toList());
System.out.println(collect);
4.常见终止操作
4.1查找和匹配
- allMatch:判断是否都匹配
List<Integer> integers = Arrays.asList(1, 2, 3);
//是否都大于2
boolean b = integers.stream().allMatch(num -> num > 2); // false
- anyMatch:是否有一个匹配
List<Integer> integers = Arrays.asList(1, 2, 3);
//是否有一个大于2
boolean b = integers.stream().anyMatch(num -> num > 2); // true
- noneMatch:判断是否都没有匹配
List<Integer> integers = Arrays.asList(1, 2, 3);
//是否都不大于5
boolean b = integers.stream().noneMatch(num -> num > 5);//true
System.out.println(b);
- findFirst:返回第一个元素
List<Integer> integers = Arrays.asList(1, 2, 3);
Optional<Integer> first = integers.stream().findFirst();
System.out.println(first.get());
- findAny:返回流中的任意一个
注意:串行的情况下,一般只会返回第一个结果,并行的情况下是任意一个,但是一般会固定那一个
```
System.out.println(IntStream.range(0, 100).parallel().findAny().getAsInt()); // 65
```
- count:返回流中的数量
long count = IntStream.range(0, 100).count();//100
- max:返回流中最大的
int max = IntStream.range(0, 100).max().getAsInt();
System.out.println(max);// 99
- min:返回流中最小的
int min = IntStream.range(0, 100).min().getAsInt();
System.out.println(min); // 0
4.2归约
- reduce:将上一次的结果和当前值相处理
Optional<T> reduce(BinaryOperator<T> accumulator);
// x:第一次的值是第一个元素,之后是上一次处理完的结果,y:第一次的值是第二个元素,之后当前遍历到的值
//1. x:1,y:2
//2. x:3,y:3
//3. x:6,y:4
//4. x:10,y:5
// 求1-10的和
OptionalInt reduce = IntStream.range(1, 11).reduce((x, y) -> x + y); // 55
T reduce(T identity, BinaryOperator<T> accumulator);
// x:第一次的值是identity,之后是上一次处理完的结果,y:当前遍历到的值
//1. x:0,y:1
//2. x:1,y:2
//3. x:3,y:3
//4. x:6,y:4
//5. x:10,y:5
// 求1-10的和
int reduce = IntStream.range(1, 11).reduce(0, (x, y) -> x + y); // 55
4.3收集
- collect:将流转换成其他形式
<R> R collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner);
supplier:创建收集的容器
accumulator:第一个参数是:最初创建的容器,第二个参数是遍历到的数据
combiner:这个参数只有在并行的时候才会操作,作用是将两个容器合并,下面的例子种,这两个参数都是数组
ArrayList<Integer> collect = IntStream.range(1, 11).parallel().collect(ArrayList::new, (list, num) -> list.add(num), (l1, l2) -> l1.addAll(l2));
//简化写法
//ArrayList<Integer> collect = IntStream.range(1, 11).parallel().collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
System.out.println(collect); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
<R, A> R collect(Collector<? super T, A, R> collector);
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);
List<Integer> collect = integers.stream().collect(Collectors.toList());
常用的Collectors方法
- toCollection:指定转换的集合类型
HashSet<Integer> collect = Stream.of(1, 2, 3).collect(Collectors.toCollection(HashSet::new));
System.out.println(collect); // [1, 2, 3]
- toSet/toList:将集合转换成HashSet和ArrayList
HashSet<Integer> set = Stream.of(1, 2, 3).collect(Collectors.toSet());
List<Integer> list = Stream.of(1, 2, 3).collect(Collectors.toSet());
- joining:可以将流中的数据拼接成字符串
// 默认直接拼接
public static Collector<CharSequence, ?, String> joining()
// 添加分隔符进行拼接
public static Collector<CharSequence, ?, String> joining(CharSequence delimiter)
// 添加分隔符进行拼接,并且指定前后缀
public static Collector<CharSequence, ?, String> joining(CharSequence delimiter, CharSequence prefix, CharSequence suffix)
Stream.of("1", "2", "3").collect(Collectors.joining()); // 123
Stream.of("1", "2", "3").collect(Collectors.joining(",")); // 1,2,3
Stream.of("1", "2", "3").collect(Collectors.joining(",", "[", "]")); // [1,2,3]
- groupingBy:对流中的数据以某个属性进行分组
groupingBy(Function<? super T, ? extends K> classifier)@Data public class Fruit { private String name; private Integer price; } List<Fruit> fruitList = Arrays.asList( new Fruit("apple", 6), new Fruit("apple", 6), new Fruit("banana", 7), new Fruit("banana", 7), new Fruit("banana", 7), new Fruit("grape", 8));
底层以List的方式收集各个分组的数据
classifier:分类的方式
groupingBy(Function<? super T, ? extends K> classifier, Collector<? super T, A, D> downstream)//{banana=[stream.Fruit@7b23ec81, stream.Fruit@6acbcfc0, stream.Fruit@5f184fc6], apple=[stream.Fruit@3feba861, stream.Fruit@5b480cf9], grape=[stream.Fruit@6f496d9f]} Map<String, List<Fruit>> collect = fruitList.stream().collect(Collectors.groupingBy(Fruit::getName));
classifier:分类的方式
downstream:收集各个分组的方式//{banana=3, apple=2, grape=1},以name去分组,并统计数量 Map<String, Long> collect = fruitList.stream().collect(Collectors.groupingBy(Fruit::getName, Collectors.counting())); //{banana=[7], apple=[6], grape=[8]},以name去分组,并将价格转成set Map<String, Set<Integer>> collect = fruitList.stream().collect(Collectors.groupingBy(Fruit::getName, Collectors.mapping(Fruit::getPrice, Collectors.toSet())));
- groupingByConcurrent:由于groupingBy是非线程安全的,而该方法则是groupingBy方法的线程安全版本,默认情况下,返回的Map类型是ConcurrentHashMap
- toMap:将流转换成Map对象
toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper)
注意:该方法下,生成Map的Key不能重复,不然会抛异常
```
List<Fruit> fruitList = Arrays.asList(new Fruit("apple", 6), new Fruit("banana", 7), new Fruit("grape", 8));
//{banana=7, apple=6, grape=8}
Map<String, Integer> collect = fruitList.stream().collect(Collectors.toMap(Fruit::getName, Fruit::getPrice));
```
*toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper,BinaryOperator<U> mergeFunction)*
mergeFunction:如何处理存在Key重复的问题,lambda表达式的第一个代表旧值,第二个代表新值
例如:
(o, n) -> o + n:老值+新值
(o, n) -> o :使用老值
(o, n) -> n :使用新值
```
List<Fruit> fruitList = Arrays.asList(
new Fruit("apple", 6), new Fruit("apple", 6),
new Fruit("banana", 7), new Fruit("banana", 7),
new Fruit("banana", 7), new Fruit("grape", 8));
//{banana=21, apple=12, grape=8}
Map<String, Integer> collect = fruitList.stream().collect(Collectors.toMap(Fruit::getName, Fruit::getPrice, (o, n) -> o + n));
```
*toMap(Function<? super T, ? extends K> keyMapper,Function<? super T, ? extends U> valueMapper, BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier)*
```
List<Fruit> fruitList = Arrays.asList(
new Fruit("apple", 6), new Fruit("apple", 6),
new Fruit("banana", 7), new Fruit("banana", 7),
new Fruit("banana", 7), new Fruit("grape", 8));
//{apple=6, banana=7, grape=8}
Map<String, Integer> collect = fruitList.stream().collect(Collectors.toMap(Fruit::getName, Fruit::getPrice, (o, n) -> n,TreeMap::new));
```
- toConcurrentMap:toMap方法的线程安全版本
- summarizingInt/summarizingLong/summarizingDouble方法:生成统计数据
List<Fruit> fruitList = Arrays.asList(new Fruit("apple", 6), new Fruit("banana", 7), new Fruit("grape", 8));
IntSummaryStatistics collect = fruitList.stream().collect(Collectors.summarizingInt(Fruit::getPrice));
//max:8 min:6 sum:21
System.out.println("max:"+collect.getMax()+" min:"+collect.getMin()+" sum:"+collect.getSum());
- summingInt/summingLong/summingDouble/averagingInt/averagingLong/averagingDouble方法
相当于上面统计出来的结果直接取值
Integer sum = fruitList.stream().collect(Collectors.averagingInt(Fruit::getPrice)); //21
- partitioningBy方法
该方法也是用于分组,不过是根据某一条件进行分组,最终分成满足条件的true和不满足条件的false两个分组,返回类型是Map<Boolean, Object>
Map<Boolean, List<Integer>> collect = Stream.of(1, 2, 3).collect(Collectors.partitioningBy(num -> num > 2));
System.out.println(collect);//{false=[1, 2], true=[3]}
- reducing方法
Stream.of(1, 2, 3).collect(Collectors.reducing(0, (x, y) -> x + y));
// 等同于
Stream.of(1, 2, 3).reduce(0, (x, y) -> x + y);
- maxBy/minBy方法
通过比较器自定义比较方法
Optional<Integer> collect = Stream.of(1, 2, 3).collect(Collectors.maxBy((x, y) -> x - y)); // 3
//等同于
Optional<Integer> collect = Stream.of(1, 2, 3).max((x, y) -> x - y);
- counting方法
Optional<Integer> collect = Stream.of(1, 2, 3).collect(Collectors.counting()); // 3
//等同于
Optional<Integer> collect = Stream.of(1, 2, 3).count();
- collectingAndThen方法
该方法接收两个参数,表示在第一个参数执行基础上,再执行第二个参数对应的函数表达式
//先取平均再乘10
Double collect = Stream.of(1, 2, 3).collect(Collectors.collectingAndThen(Collectors.averagingInt(num -> num), num -> num * 10)); // 20.0
- mapping方法
//每个元素+1
List<Integer> collect = Stream.of(1, 2, 3).collect(Collectors.mapping(num -> num + 1, Collectors.toList())); //[2, 3, 4]
5.小测试
1.请问:
- 解释一下以下代码的作用
- 最后输出什么
List<String> strings = Arrays.asList("ABCD", "abccd", "ABcDw", "abCD", "AAA");
strings.stream()
.map(String::toUpperCase)
.filter("ABCDEFGH"::startsWith)
.map(String::toLowerCase)
.distinct()
.forEach(System.out::println);
2.有以下的代码,问
1) 代码能否编译通过?
2) 如果能编译通过,那么运行之后会输出什么?为什么?如果不能编译通过,为什么?
List<String> list = Arrays.asList("123", "123", "456", "123");
list.stream()
.filter("123"::equals)
.filter(str->{
System.out.println(str);
return true;
});
答案
第一题:
1)先将strings数组转换成大写,然后过滤出str -> "ABCDEFGH".startsWith(str)相匹配的字符串,接着将过滤出的数据转换成小写,然后去重,最后遍历输出每个数据
2)最后输出abcd
第二题:
1)代码可以编译通过
2)运行不会有任何输出,因为StreamAPI包括,创建Stream,中间操作,终止操作,因为这里没有终止操作,所以不会有任何输出