学习Java8--stream

1 为什么引入流

先从一个例子开始,看看为什么在Java8中要引入流(Stream)?
比如实现这么一个需求:在学生集合中查找男生的数量。
传统的写法为:

public  long  getCountsOf
MaleStudent(List<Student> students) {
     long  count =  0 ;
     for  (Student student : students) {
         if  (student.isMale()) {
             count++;
         }
     }
     return  count;
}

看似没什么问题,因为我们写过太多类似的“样板”代码,尽管智能的IDE通过code template功能让这一枯燥过程变得简化,但终究不能改变冗余代码的本质。
再看看使用流的写法:

public  long  getCountsOfMaleStudent(List<Student> students) {
     return  students.stream().filter(Student::isMale).count();
}

一行代码就把问题解决了!
虽然读者可能还不太熟悉流的语法特性,但这正是函数式编程思想的体现:

  • 回归问题本质,按照心智模型思考问题。
  • Stream提供的高阶函数以及链式调用,可以对数据进行更高层的抽象和控制。
  • 很容易转成并行流,提供并行计算的能力。
  • 延迟加载。
  • 简化代码。

2 创建流

创建流的方式可以有很多种,其中最常见的方式是通过Collection的Stream()方法或者Arrays的Stream()方法来生成流。

  • 比如:

    List<Integer> numbers = Arrays.asList( 1 2 3 );
    Stream<Integer> numberStream = numbers.stream();
     
    String[] words =  new  String[]{ "one" "two" };
    Stream<String> wordsStream = Arrays.stream(words);

    当然Stream接口本身也提供了许多和流相关的操作。

    // 创建流
    Stream<Integer> numbers = Stream.of( 1 2 3 );
    // 创建空流
    Stream<String> emptyStream = Stream.empty();
    // 创建一个元素为“hi”的无限流
    Stream<String> infiniteString = Stream.generate(() ->  "hi" );
    // 创建一个从0开始的递增无限流
    Stream<BigInteger> integers = Stream.iterate(BigInteger.ZERO, n -> n.add(BigInteger.ONE));

    其中Stream.generate()和Stream.iterate()产生的都是无限流,如果要把他们截取为有限流,可以使用limit()方法,
    比如:

    Stream<Double> top10 = Stream.generate(Math::random).limit( 10 );

    另外,可以通过skip()方法跳过元素,concat()方法连接两个流。

    Stream<Integer> skipedStream = Stream.of( 1 2 3 4 ).skip( 2 );  // 3, 4
    Stream<String> concatedStream = Stream.concat(Stream.of( "hello" ), Stream.of( ",world" ));  // hello,world

3 常用的流操作

filter

filter()方法的作用就是根据输入的条件表达式过滤元素。
接口定义如下:

Stream<T> filter(Predicate<?  super  T> predicate);

从中可以看出,输入参数是一个Predicate,也即是一个条件表达式。
一个例子:

Stream.of( "a" "1b" "c" "0x" ).filter(value -> isDigit(value.charAt( 0 )));

过滤出第一个字符是数字的元素。
输出结果为:

1b, 0x

map

map()的主要作用是通过映射函数转换成新的数据。
接口定义如下:

<R> Stream<R> map(Function<?  super  T, ?  extends  R> mapper);

从中可以看出,输入参数是一个Function。
一个例子:

Stream.of( "a" "b" "c" ).map(String::toUpperCase);

把字符串转换成大写。
输出结果:

A, B, C

有状态的转换

在前面介绍的函数中,无论是map还是filter,都不会改变流的状态,也即结果并不依赖之前的元素。
除此之外,Java8也提供了有状态的转换,常用的操作是distinct和sorted。

distinct

distinct()的主要作用是去除流中的重复元素。和Oracle的distinct一个作用。
举例如下:

Stream<String> distinctStream = Stream.of( "one" "one" "two" "three" ).distinct();

去除字符串中的重复元素,返回结果为:

one, two, three

sorted

sorted()的主要作用是对流按照指定的条件进行排序。
接口定义如下:

Stream<T> sorted(Comparator<?  super  T> comparator);

从中可以看出,入参是一个Comparator,也即是一个函数式接口。
一个例子:

Stream<String> sortedStream = Stream.of( "one" "two" "three" ).sorted(Comparator.comparing(String::length).reversed());

对字符串按照长度进行降序排列。
注意,这里使用了Comparator.comparing方法来简化调用。
输出结果为:

[three, one, two]

4 分组操作(groupingBy)

groupingBy操作也是基于collect操作完成的,功能是根据条件进行分组操作,他和partitioningBy不同的一点是,它的输入是一个Function,这样返回结果的Map中的Key就不再是boolean型,而是符合条件的分组值,使用场景会更广泛。
接口定义如下:

public  static  <T, K> Collector<T, ?, Map<K, List<T>>> groupingBy(Function<?  super  T, ?  extends  K> classifier)

一个例子

public  Map<String, List<Student>> studentByName(Stream<Student> students) {
     return  students.collect(Collectors.groupingBy(student -> student.getName()));
}

按照学生的姓名进行分组。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值