Java 8编程进阶-Stream之函数式编程

1、什么是Stream

       Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。

    总之,中间操作只是创建另一个流,不会执行任何处理,直到最终操作被调用。一旦最终操作被调用,则开始遍历所有的流,并且相关的函数会逐一应用到流上。中间操作是惰性操作,所以,流支持惰性,下面是一些函数的分类。


Stream操作分类
中间操作(Intermediate operations)无状态(Stateless)unordered() filter() map() mapToInt() mapToLong() mapToDouble() flatMap() flatMapToInt() flatMapToLong() flatMapToDouble() peek()
有状态(Stateful)distinct() sorted() sorted() limit() skip()
最终操作(Terminal operations)非短路操作forEach() forEachOrdered() toArray() reduce() collect() max() min() count()
短路操作(short-circuiting)anyMatch() allMatch() noneMatch() findFirst() findAny()


2、流的使用

2.1、获取Stream

在使用流之前,首先需要拥有一个数据源,并通过StreamAPI提供的一些方法获取该数据源的流对象。数据源可以有多种形式:

(1)集合 
这种数据源较为常用,通过stream()方法即可获取流对象:

   List<String> list = new ArrayList<>();
   Stream<String> stream = list.stream();

(2)数组 
通过Arrays类提供的静态函数stream()获取数组的流对象:

   String[] array = {"a","b","c"};
   Stream<String> stream = Arrays.stream(array);

(3)值 
直接将几个值变成流对象:

   Stream<String> stream = Stream.of("a""b""c");


2.2、forEach

对流中的每个元素执行一些操作。

    List<String> list = new ArrayList<>(Arrays.asList("a","b","c"));
    //写法一
    list.stream().forEach(x -> {
        System.out.println(x);
    });
    //写法二
    list.stream().forEach(System.out::println);

2.3、map

接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素

    List<String> list = new ArrayList<>(Arrays.asList("a","b","c"));
    //写法一
    list.stream().forEach(x -> {
        System.out.println(x);
    });
    //写法二
    list.stream().forEach(System.out::println);

2.4、flatMap

接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。

    List<String> list = new ArrayList<>(Arrays.asList("a,1","b,2","c,3"));

    List<String> newList = list.stream().flatMap(x->{
        String[] array = x.split(",");
        return Arrays.stream(array);
    }).collect(Collectors.toList());

结果:

[a, 1, b, 2, c, 3]

2.5、reduce()

通过一个二进制操作将流中的元素合并到一起。

    List<Integer> list = new ArrayList<>(Arrays.asList(123));
    //写法一,累加不带初始值
    Optional accResult = list.stream().reduce((acc, item) -> {
        acc += item;
        return acc;
    });

输出6

    //写法一,累加带初始值10
    int result1 = list.stream().reduce(10(acc, item) -> {
        acc += item;
        return acc;
    });

输出16

2.6、filter()

过滤元素

    List<Integer> list = new ArrayList<>(Arrays.asList(123,4));
    List<Integer> newList = list.stream().filter(x -> x >=3).collect(Collectors.toList());

输出[3,4]

2.7、distinct()

去除重复元素

    List<Integer> list = new ArrayList<>(Arrays.asList(111,2,2));
    List<Integer> newList = list.stream().distinct().collect(Collectors.toList());

输出结果

[1,2]

2.8、collect() 

将流中的元素倾倒入某些容器

(1)toList

将元素转成List,很简单,上面的实例很多

(2)toSet

将元素转成Set

    List<Integer> list = new ArrayList<>(Arrays.asList(11122));
    Set<Integerset = list.stream().collect(Collectors.toSet());

 (3) toMap

将元素转成Map

用法可以如下

public Map<Long, String> getIdNameMap(List<Account> accounts) {

return accounts.stream().collect(Collectors.toMap(Account::getId, Account::getUsername));
}

收集成实体本身map

代码如下:

public Map<Long, Account> getIdAccountMap(List<Account> accounts) {
return accounts.stream().collect(Collectors.toMap(Account::getId, account -> account));
}

account -> account是一个返回本身的lambda表达式,其实还可以使用Function接口中的一个默认方法代替,使整个方法更简洁优雅:

public Map<Long, Account> getIdAccountMap(List<Account> accounts) {
return accounts.stream().collect(Collectors.toMap(Account::getId, Function.identity()));
}

重复key的情况

代码如下:

public Map<String, Account> getNameAccountMap(List<Account> accounts) {
return accounts.stream().collect(Collectors.toMap(Account::getUsername, Function.identity()));
}

这个方法可能报错(java.lang.IllegalStateException: Duplicate key),因为name是有可能重复的。toMap有个重载方法,可以传入一个合并的函数来解决key冲突问题:

public Map<String, Account> getNameAccountMap(List<Account> accounts) {
return accounts.stream().collect(Collectors.toMap(Account::getUsername, Function.identity()(key1, key2) -> key2));
}

这里只是简单的使用后者覆盖前者来解决key重复问题。

指定具体收集的map

toMap还有另一个重载方法,可以指定一个Map的具体实现,来收集数据:

public Map<String, Account> getNameAccountMap(List<Account> accounts) {
return accounts.stream().collect(Collectors.toMap(Account::getUsername, Function.identity()(key1, key2) -> key2LinkedHashMap::new));
}

2.8、min()  
根据一个比较器找到流中元素的最小值。

BigDecimal min = planList.stream().min((a,b)->a.getPrice().compareTo(b.getPrice())).get().getPrice();

2.9、max() 

根据一个比较器找到流中元素的最大值。

BigDecimal max = planList.stream().max((a,b)->a.getPrice().compareTo(b.getPrice())).get().getPrice();

2.10、count() 

计算流中元素的数量。

    List<Integer> list = new ArrayList<>(Arrays.asList(14233,101,6));
    long min = list.stream().distinct().count();


©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页