Java函数式编程二之常用流操作

Java函数式编程一
函数式开发旨在帮助程序员写出更好的代码,如Java8引入的流(Stream)使程序员得以站在更高的抽象层次对集合进行操作!Stream 是用函数式编程方式在集合类上进行复杂操作的工具。
传统循环操作有很多弊端如需要写很多样板代码、行为意图不清晰等,嵌套循环更严重,另外传统操作对于并行开发也很麻烦!使用Stream对集合进行操作,可以避免这些。如:
计算集合中大于8的元素个数

List<Integer> list = Arrays.asList(2, 5, 3, 9, 25, 88);
//传统操作
int count = 0;
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()){
    Integer integer = iterator.next();
    if (integer > 8){
        count++;
    }
}
//使用Stream 可被分解为两步更简单的操作  行为更加明确
//1、过滤 2、计算
 long count = list.stream()
                .filter(item -> item > 8)
                .count();

实现机制

上面Stream的例子有两个行为,但是其内部只进行了一次循环,这里涉及两个概念惰性求值方法及早求值方法
第一个行为

list.stream()
        .filter(item -> item > 8)

这里其实未做什么实际性的工作,只是描述了Stream,最终不产生新的集合,像filter这样的方法就叫做惰性求值方法;而像count()这样最终会从Stream产生值的方法叫及早求值方法。
这个可以从侧面证明一下,如在过滤器中加入println语句,来输出元素:

  list.stream()
	    .filter(item -> {
	        System.out.println(item);
	        return item > 8;
	    });

可以发现是没有任何元素输出的,只有加上及早求值方法,才会有输出!
判断一个操作是惰性求值还是及早求值很简单:只需看它的返回值。如果返回值是 Stream, 那么是惰性求值;如果返回值是另一个值或为空,那么就是及早求值。使用这些操作的理想方式就是形成一个惰性求值的链,最后用一个及早求值的操作返回想要的结果!

常用的流操作

collect(Collectors.toList())

collect(toList()) 方法由 Stream 里的值生成一个列表,是一个及早求值操作。

//Stream 的 of 方法使用一组初始值生成新的 Stream
List<Integer> collect =  Stream.of(9,6,8,25,88) 
                .filter(item -> item > 8)
                .collect(Collectors.toList());
System.out.println(collect);                
[9, 25, 88]

map

如果有一个函数可以将一种类型的值转换成另外一种类型,那么map 操作就可以使用该函数,将一个流中的值转换成一个新的流。
在这里插入图片描述

//接受一个Function类型的lambda表达式  该类型函数接口上一篇博文有介绍
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
//将集合中每个元素的第一个字母a替换为A
List<String> collect2 = Stream.of("a", "world", "Java")
                .map(s -> s.replaceFirst("a", "A"))
                .collect(Collectors.toList());
System.out.println(collect2);
[A, world, JAva, AAr]

filter

遍历数据并检查其中的元素时,可尝试使用 Stream 中提供的新方法 filter .
在这里插入图片描述

//接受一个Predicate类型的接口
Stream<T> filter(Predicate<? super T> predicate);
//找出一组字符串中以数字开头的
List<String> collect3 = Stream.of("1aa", "abc", "a3b")
        .filter(s -> Character.isDigit(s.charAt(0)))
        .collect(Collectors.toList());
System.out.println(collect3);
[1aa]

flatMap

flatMap 方法可用 Stream 替换值,然后将多个 Stream 连接成一个 Stream .
在这里插入图片描述

List<Integer> list1 = Arrays.asList(1, 3, 5);
List<Integer> list2 = Arrays.asList(2, 4, 6);
List<Integer> list3 = Arrays.asList(3, 6, 9);
List<Integer> collect4 = Stream.of(list1, list2, list3)
        .flatMap(number -> number.stream())
        .collect(Collectors.toList());
System.out.println(collect4);
[1, 3, 5, 2, 4, 6, 3, 6, 9]

max和min

求最大值和最小值

Optional<T> min(Comparator<? super T> comparator);
Optional<T> max(Comparator<? super T> comparator);
//求最小值
Integer integer = Stream.of(2, 5, 8, 3)
                .min(Comparator.comparing(item -> item))
                .get();
System.out.println(integer);
//找出最长的字符串
String s = Stream.of("as", "java", "hello")
        .max(Comparator.comparing(item -> item.length()))
        .get();
System.out.println(s);
2
hello

reduce

reduce 操作可以实现从一组值中生成一个值。在上述例子中用到的 count、min 和 max 方 法,因为常用而被纳入标准库中。事实上,这些方法都是 reduce 操作。

 Integer result = Stream.of(2, 8, 5)
                .reduce(0, (acc, element) -> acc + element);
 System.out.println(result);

reduce不太好理解,上面代码就相当于下面的实现,reduce的第一个参数0相当于下面的initialValue的值,第二个参数是一个lambda表达式,里面的acc是一个累加器相当于下面的result!

int result = initialValue;
for (Integer element : collection) {
    result= result+ element;
}

运行解果

15

reduce方法有两种形式,一种就是上面提到的有一个初始值,另一种不需要初始值。没有初始值的reduce第一步使用的前面两个元素,区别与有初始值操作的每一步只使用一个元素!

如何使用Stream

Stream接口的方法有很多,有时候会让人难以选择,这时候就要先明确问题,然后将问题拆分为简单的 Stream 操作,最后找出每一步对应的 Stream API 。具体看下面的几个例子:

找出集合元素中偶数的最大值

 /**
   * 1、找出偶数 -> filter
   * 2、找最大值 -> max
   */
  Integer integer = Stream.of(3, 6, 9, 8, 4)
          .filter(i -> i % 2 == 0)
          .max(Comparator.comparing(item -> item))
          .get();
  System.out.println(integer);
8

找出所有学号以2018开头的学生的手机号

 /**
   * 1、找出目标学生 -> filter
   * 2、使用 map 方法将学生转换为手机号-> map
   */
 studentList.stream()
                .filter(student -> student.getId().startsWith("2018"))
                .map(student-> student.getPhone())
                .collect(Collectors.toList());

找出各个班级学生年龄大于18的学生姓名

 /**
   * 1、将各个班级的学生连成一个Stream -> flatMap
   * 2、找出大于18的学生
   * 3、将学生转换为name
   */
List<String> collect = classList.stream()
                .flatMap(c -> c.getStudents().stream())
                .filter(student -> student.getAge() > 18)
                .map(student -> student.getName())
                .collect(Collectors.toList());
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值