JAVA8之Stream API

1.StreamAPI可以做什么

StreamAPI为我们提供了强大的集合操作,同时StreamAPI操作简单,代码直观,容易上手。

2.Stream的操作步骤

List integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7);

1.创建Stream

Stream stream = integers.stream();

2.中间操作

注意:没有终止操作是不会执行中间操作的

Stream integerStream = stream.filter(num -> {
  System.out.println("current num is " + num);
  return num > 2;
});

3.终止操作

List collect = integerStream.collect(Collectors.toList());
System.out.println(collect);//[3, 4, 5, 6, 7]

综合成一条语句就是

List collect = integers.stream().filter(num -> {
  System.out.println("current num is " + num);
  return num > 2;
}).collect(Collectors.toList());

3.常见的中间操作

3.1筛选和切片

  • filter:过滤出某些元素
List integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
//filter(num -> num > 2),此处是过滤出大于2的元素
List collect = integers.stream().filter(num -> num > 2).collect(Collectors.toList());
System.out.println(collect);//[3, 4, 5, 6, 7]
  • limit:过滤出某些元素
List integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
//limit(3),获取过滤出的前3个元素
List collect = integers.stream().filter(num -> num > 2).limit(3).collect(Collectors.toList());
System.out.println(collect);//[3, 4, 5]
  • skip:跳过多少个元素
List integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
//skip(2).limit(3),此处是先跳过2个元素再剩余流中的3个元素
List collect = integers.stream().filter(num -> num > 2).skip(2).limit(3).collect(Collectors.toList());//[5, 6, 7]
//limit(3).skip(2),此处是获取流中的前面3个元素,再跳过两个
List collect = integers.stream().filter(num -> num > 2).limit(3).skip(2).collect(Collectors.toList());//[5]
  • distinct:去除重复
List integers = Arrays.asList(1, 3, 3, 3, 7);
List collect = integers.stream().distinct().collect(Collectors.toList());
System.out.println(collect);//[1, 3, 7]

3.2映射

  • map:将元素转映射成其他形式的元素
List integers = Arrays.asList(1, 2, 3);
List collect = integers.stream().map(num -> num * 2).collect(Collectors.toList());//[2, 4, 6]
//此处将num映射成了String类,当然也可以映射成其他类
List 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 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> mapper);
//Arrays.stream(T[] array)
//flatMap的返回值是一个流所以这里使用Arrays.stream(),这里可以使用静态引用的方式Arrays::stream
//List resultCollect = collect.stream().flatMap(Arrays::stream).collect(Collectors.toList());
List 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 integers = Arrays.asList(1, 9, 3, 77, 52, 7);
List collect = integers.stream().sorted().collect(Collectors.toList());
System.out.println(collect);
  • sorted(Comparator comparator):自定义排序
//降序排序
List integers = Arrays.asList(1, 9, 3, 77, 52, 7);
List collect = integers.stream().sorted((n1, n2) -> n2 - n1).collect(Collectors.toList());
System.out.println(collect);

4.常见终止操作

4.1查找和匹配

  • allMatch:判断是否都匹配
List integers = Arrays.asList(1, 2, 3);
//是否都大于2
boolean b = integers.stream().allMatch(num -> num > 2); // false
  • anyMatch:是否有一个匹配
List integers = Arrays.asList(1, 2, 3);
//是否有一个大于2
boolean b = integers.stream().anyMatch(num -> num > 2); // true
  • noneMatch:判断是否都没有匹配
List integers = Arrays.asList(1, 2, 3);
//是否都不大于5
boolean b = integers.stream().noneMatch(num -> num > 5);//true
System.out.println(b);
  • findFirst:返回第一个元素
List integers = Arrays.asList(1, 2, 3);
Optional 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 reduce(BinaryOperator 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 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 collect(Supplier supplier, BiConsumer accumulator, BiConsumer combiner);
    supplier:创建收集的容器
    accumulator:第一个参数是:最初创建的容器,第二个参数是遍历到的数据
    combiner:这个参数只有在并行的时候才会操作,作用是将两个容器合并,下面的例子种,这两个参数都是数组
ArrayList collect = IntStream.range(1, 11).parallel().collect(ArrayList::new, (list, num) -> list.add(num), (l1, l2) -> l1.addAll(l2));
//简化写法
//ArrayList 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 collect(Collector collector);

List integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);
List collect = integers.stream().collect(Collectors.toList());

常用的Collectors方法

  • toCollection:指定转换的集合类型
HashSet collect = Stream.of(1, 2, 3).collect(Collectors.toCollection(HashSet::new));
System.out.println(collect); // [1, 2, 3]
  • toSet/toList:将集合转换成HashSet和ArrayList
HashSet set = Stream.of(1, 2, 3).collect(Collectors.toSet());
List list = Stream.of(1, 2, 3).collect(Collectors.toSet());
  • joining:可以将流中的数据拼接成字符串
// 默认直接拼接
public static Collector joining()
// 添加分隔符进行拼接
public static Collector joining(CharSequence delimiter)
// 添加分隔符进行拼接,并且指定前后缀
public static Collector 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:对流中的数据以某个属性进行分组
    @Data
    public class Fruit {
        private String name;
        private Integer price;
    }
    
    List 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));
    
    groupingBy(Function classifier)
    底层以List的方式收集各个分组的数据
    classifier:分类的方式
    //{banana=[stream.Fruit@7b23ec81, stream.Fruit@6acbcfc0, stream.Fruit@5f184fc6], apple=[stream.Fruit@3feba861, stream.Fruit@5b480cf9], grape=[stream.Fruit@6f496d9f]}
    Map> collect = fruitList.stream().collect(Collectors.groupingBy(Fruit::getName));
    
    groupingBy(Function classifier, Collector downstream)
    classifier:分类的方式
    downstream:收集各个分组的方式
    //{banana=3, apple=2, grape=1},以name去分组,并统计数量
    Map collect = fruitList.stream().collect(Collectors.groupingBy(Fruit::getName, Collectors.counting()));
    //{banana=[7], apple=[6], grape=[8]},以name去分组,并将价格转成set
    Map> collect = fruitList.stream().collect(Collectors.groupingBy(Fruit::getName, Collectors.mapping(Fruit::getPrice, Collectors.toSet())));
    
  • groupingByConcurrent:由于groupingBy是非线程安全的,而该方法则是groupingBy方法的线程安全版本,默认情况下,返回的Map类型是ConcurrentHashMap
  • toMap:将流转换成Map对象
    toMap(Function keyMapper, Function valueMapper)

注意:该方法下,生成Map的Key不能重复,不然会抛异常

```
List fruitList = Arrays.asList(new Fruit("apple", 6), new Fruit("banana", 7), new Fruit("grape", 8));
//{banana=7, apple=6, grape=8}
Map collect = fruitList.stream().collect(Collectors.toMap(Fruit::getName, Fruit::getPrice));
```
*toMap(Function keyMapper, Function valueMapper,BinaryOperator mergeFunction)*
mergeFunction:如何处理存在Key重复的问题,lambda表达式的第一个代表旧值,第二个代表新值
例如:
(o, n) -> o + n:老值+新值
(o, n) -> o :使用老值
(o, n) -> n :使用新值
```
List 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 collect = fruitList.stream().collect(Collectors.toMap(Fruit::getName, Fruit::getPrice, (o, n) -> o + n));
```
*toMap(Function keyMapper,Function valueMapper, BinaryOperator mergeFunction, Supplier mapSupplier)*
```
List 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 collect = fruitList.stream().collect(Collectors.toMap(Fruit::getName, Fruit::getPrice, (o, n) -> n,TreeMap::new));
```
  • toConcurrentMap:toMap方法的线程安全版本
  • summarizingInt/summarizingLong/summarizingDouble方法:生成统计数据
List 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
Map> 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 collect = Stream.of(1, 2, 3).collect(Collectors.maxBy((x, y) -> x - y)); // 3
//等同于
Optional collect = Stream.of(1, 2, 3).max((x, y) -> x - y);
  • counting方法
Optional collect = Stream.of(1, 2, 3).collect(Collectors.counting()); // 3
//等同于
Optional 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 collect = Stream.of(1, 2, 3).collect(Collectors.mapping(num -> num + 1, Collectors.toList())); //[2, 3, 4]

5.小测试

1.请问:

  1. 解释一下以下代码的作用
  2. 最后输出什么
List 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 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,中间操作,终止操作,因为这里没有终止操作,所以不会有任何输出

你可能感兴趣的:(JAVA8之Stream API)