Stream

简介

流是Java API的新成员,它允许你以声明性方式处理数据集合(通过查询语句来表达,而不是临时编写一个实现)。就现在来说,你可以把它们看成遍历数据集的高级迭代器。此外,流还可以透明地并行处理。

流是从支持数据处理操作的源生成的元素序列。

特点

  1. 元素序列——就像集合一样,流也提供了一个接口,可以访问特定元素类型的一组有序值。因为集合是数据结构,所以它的主要目的是以特定的时间/空间复杂度存储和访问元素(如ArrayList 与 LinkedList)。但流的目的在于表达计算。
  2. 源——流会使用一个提供数据的源,如集合、数组或输入/输出资源。 请注意,从有序集合生成流时会保留原有的顺序。由列表生成的流,其元素顺序与列表一致。
  3. 数据处理操作——流的数据处理功能支持类似于数据库的操作,以及函数式编程语言中的常用操作,如filter、map、reduce、find、match、sort等。流操作可以顺序执行,也可并行执行。

此外,流操作有两个重要的特点。

  1. 流水线——很多流操作本身会返回一个流,这样多个操作就可以链接起来,形成一个大的流水线。
  2. 内部迭代——与使用迭代器显式迭代的集合不同,流的迭代操作是在背后进行的。

优势

  1. 声明性——更简洁,更易读
  2. 可复合——更灵活
  3. 可并行——性能更好

只能遍历一次

请注意,和迭代器类似,流只能遍历一次。遍历完之后,我们就说这个流已经被消费掉了。你可以从原始数据源那里再获得一个新的流来重新遍历一遍,就像迭代器一样。

List<String> title = Arrays.asList("Java8", "In", "Action");
Stream<String> s = title.stream();
s.forEach(System.out::println);
//java.lang.IllegalStateException
s.forEach(System.out::println);

流操作

img

流的使用一般包括三件事:

  1. 一个数据源(如集合)来执行一个查询;
  2. 一个中间操作链,形成一条流的流水线;
  3. 一个终端操作,执行流水线,并能生成结果。

中间操作API

中间操作返回的都是流(Stream).

操作 作用 操作参数
filter 过滤 Predicate
map 映射 Funcation<T,R>
limit 截断, 方法会返回一个不超过给定长度的流。所需的长度作为参数传递 给limit。如果流是有序的,则最多会返回前n个元素。
sorted 排序 Comparator
distinct 去重
flatMap 先映射后扁平化 Function<T, Stream>
skip 跳过 long

终端(聚合)操作API

终端操作返回的都是最终结果。

操作 目的
forEach 消费流中的每个元素并对其应用 Lambda。这一操作返回 void
count 返回流中元素的个数。这一操作返回 long
collect 把流归约成一个集合,比如 List、Map 甚至是 Integer。
reduce 聚合求值/数据比较
allMatch 所有匹配
findAny 查找所有
findFirst 查找第一个

构建流

由值创建流

Stream<String> stream = Stream.of("Java 8 ", "Lambdas ", "In ", "Action");
stream.map(String::toUpperCase).forEach(System.out::println);

由数组、集合创建流

int[] numbers = {2, 3, 5, 7, 11, 13};
int sum = Arrays.stream(numbers).sum();
List<String> title = Arrays.asList("Java8", "In", "Action");
title.stream().forEach(System.out::println);

由文件创建流

long uniqueWords = 0;
try(Stream<String> lines =
    Files.lines(Paths.get("data.txt"), Charset.defaultCharset())){
    uniqueWords = lines.flatMap(line -> Arrays.stream(line.split(" ")))
        .distinct()
        .count();
}
catch(IOException e){

}

由函数创建流

Stream.iterate(0, n -> n + 2)
                .limit(10)
                .forEach(System.out::println);

流处理数据

收集器

java.util.stream.Collectors

@Data
@ToString
public class Dish {
    private String name;
    //素食
    private boolean vegetarian;
    //卡路里
    private int calories;
    private Type type;
    public Dish(String name, boolean vegetarian, int calories, Type type) {
        this.name = name;
        this.vegetarian = vegetarian;
        this.calories = calories;
        this.type = type;
    }
    public enum Type { MEAT, FISH, OTHER }
    public enum CaloricLevel{DIET,NORMAL,FAT}
}

筛选、截断、跳过

//筛选
List<Dish> vegetarianMenu  = menu.stream()
    .filter(Dish::isVegetarian).collect(Collectors.toList());
//返回前3个元素
List<Dish> dishes1 = menu.stream()
    .filter(d -> d.getCalories() > 300).limit(3).collect(Collectors.toList());
//跳过前两个元素
List<Dish> dishes2 = menu.stream()
    .filter(d -> d.getCalories() > 300)
    .skip(2)
    .collect(Collectors.toList());

映射、查找、归纳

//映射出Name
List<String> dishNames = menu.stream()
                .map(Dish::getName)
                .collect(toList());
//短路匹配
boolean isHealthy = menu.stream()
                .allMatch(d -> d.getCalories() < 1000);
//短路查找,查找到匹配项就返回
Optional<Dish> dish =
                menu.stream()
                        .filter(Dish::isVegetarian)
                        .findAny();
//归纳 求和
int total = menu.stream()
    .map(Dish::getCalories).reduce(0, Integer::sum);
//归纳,求最大值
Optional<Integer> max = menu.stream()
    .map(Dish::getCalories).reduce(Integer::max);

流的扁平化

List<String> title = Arrays.asList("Hello", "World");
List<String> l = title.stream()
    .map(word -> word.split(""))
    .flatMap(Arrays::stream)
    .distinct()
    .collect(toList());

img

连接、分组、分区

//连接
String shortMenu = menu.stream()
    .map(Dish::getName).collect(joining(","));

//一级分组
 Map<Dish.Type,List<Dish>> map = menu.stream()
     .collect(groupingBy(Dish::getType));

//多级分组
 Map<Dish.Type, Map<Dish.CaloricLevel, List<Dish>>> dishesByTypeCaloricLevel =
     menu.stream().collect(groupingBy(Dish::getType,groupingBy(d -> {
     if (d.getCalories() <= 400) return Dish.CaloricLevel.DIET;
     else if (d.getCalories() <=700) return Dish.CaloricLevel.NORMAL;
     else return Dish.CaloricLevel.FAT;
 })));
//分区
Map<Boolean, List<Dish>> partitionedMenu =
               menu.stream().collect(partitioningBy(Dish::isVegetarian));

并行流

可以通过对收集源调用parallelStream方法来把集合转换为并行流。并行流就是一个把内容分成多个数据块,并用不同的线程分别处理每个数据块的流。这样一来,你就可以自动把给定操作的工作负荷分配给多核处理器的所有内核。默认线程数是cpu核数

//并行流求前n个数字合
public static long parallelSum(long n) {
    //数据源
    return Stream.iterate(1L, i -> i + 1)
        //截取前n个数字
        .limit(n)
        //转换成并行流
        .parallel()
        .reduce(0L, Long::sum);
}
posted @   往事随雨  阅读(153)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
点击右上角即可分享
微信分享提示