Java8 Stream流

Java8 Stream流介绍:

Stream流是JDK8新增的成员,允许以声明性方式处理数据集合,可以把Stream流看作是遍历数据集合的一个高级迭代器。Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找/筛选/过滤、排序、聚合和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。

Stream流使用场景总结:

对于简单操作,比如最简单的遍历,Stream串行API性能明显差于显示迭代,但并行的Stream API能够发挥多核特性。
对于复杂操作,Stream串行API性能可以和手动实现的效果匹敌,在并行执行时Stream API效果远超手动实现。

出于性能考虑,
  1. 对于简单操作推荐使用外部迭代手动实现。
  2. 对于复杂操作,推荐使用Stream API。
  3. 在多核情况下,推荐使用并行Stream API来发挥多核优势。
  4. 单核情况下不建议使用并行Stream API。
出于代码简洁性考虑

使用Stream API能够写出更短的代码。

出于性能方面说

尽可能的使用Stream API也另外一个优势,那就是只要Java Stream类库做了升级优化,代码不用做任何修改就能享受到升级带来的好处。

for本来就是比较底层的操作,性能自然很高。stream的优势是并行处理。数据量越大,stream的优势越明显,数据量小,肯定是for循环性能最好。

流的介绍

  • 从支持数据处理操作的源生成元素序列.数据源可以是集合,数组或IO资源。

  • 从操作角度来看,流与集合是不同的. 流不存储数据值; 流的目的是处理数据,它是关于算法与计算的。

  • 如果把集合作为流的数据源,创建流时不会导致数据流动; 如果流的终止操作需要值时,流会从集合中获取值; 流只使用一次。

  • 流中心思想是延迟计算,流直到需要时才计算值。

在这里插入图片描述

特性:
  1. 不是数据结构,不会保存数据。
  2. 不会修改原来的数据源,它会将操作后的数据保存到另外一个对象中。(保留意见:毕竟peek方法可以修改流中元素)
  3. 惰性求值,流在中间处理过程中,只是对操作进行了记录,并不会立即执行,需要等到执行终止操作的时候才会进行实际的计算。

流的创建

通过集合生成(常用)

List<String> list = Arrays.asList("a", "b", "c");
// 创建一个顺序流
Stream<String> stream = list.stream();
// 创建一个并行流
Stream<String> parallelStream = list.parallelStream();

通过数组生成

int[] array={1,3,5,6,8};
IntStream stream = Arrays.stream(array);

使用 Stream的静态方法:of()、iterate()、generate()

Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);
 
Stream<Integer> stream = Stream.iterate(0, n -> n + 2).limit(5);
stream2.forEach(System.out::println);
 
Stream<Double> stream = Stream.generate(Math::random).limit(5);
stream3.forEach(System.out::println);

通过Stream的of方法生成流,通过Stream的empty方法可以生成一个空流.
iterate方法接受两个参数,第一个为初始化值,第二个为进行的函数操作,因为iterator生成的流为无限流,通过limit方法对流进行了截断,只生成5个偶数。
generate方法接受一个参数,方法参数类型为Supplier ,由它为流提供值。generate生成的流也是无限流,因此通过limit对流进行了截断,生成5个随机数。

通过文件生成

Stream<String> lines = Files.lines(Paths.get("data.txt"), Charset.defaultCharset());

通过Files.line方法得到一个流,并且得到的每个流是给定文件中的一行.

stream和 parallelStream的简单区分:stream是顺序流,由主线程按顺序对流执行操作,而 parallelStream是并行流,内部以多线程并行执行的方式对流进行操作,但前提是流中的数据处理没有顺序要求。例如筛选集合中的奇数,两者的处理不同之处:
在这里插入图片描述

流的操作

中间操作

一个流可以后面跟随零个或多个中间操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的,仅仅调用到这类方法,并没有真正开始流的遍历,真正的遍历需等到终端操作时,常见的中间操作有下面即将介绍的 filter、map 等。

终端操作

一个流有且只能有一个终端操作,当这个操作执行后,流就被关闭了,无法再被操作,因此一个流只能被遍历一次,若想在遍历需要通过源数据在生成流。终端操作的执行,才会真正开始流的遍历。如下面即将介绍的 count、collect 等。
在这里插入图片描述

中间操作实例

filter 筛选

通过使用filter方法进行条件筛选,filter的方法参数为一个条件(过滤保留函数返回值为 true 的元素)。

List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5, 6);
Stream<Integer> stream = integerList.stream().filter(i -> i > 3);

结果为:4,5,6

distinct 去重

通过distinct方法快速去除重复的元素。

List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5);

Stream<Integer> stream = integerList.stream().distinct();

结果为:1,2,3,4,5

limit 返回指定流个数

通过limit方法指定返回流的个数,limit的参数值必须 >=0,否则将会抛出异常。

List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5);

Stream<Integer> stream = integerList.stream().limit(3);

结果为: 1,1,2

skip 跳过流中的元素
List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5);

Stream<Integer> stream = integerList.stream().skip(2);

结果为: 2,3,4,5

skip的参数值必须>=0,否则将会抛出异常。

map 流映射

流映射就是将接受的元素映射成另外一个元素。

List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");

List<Integer> collect = stringList.stream()
        .map(String::length)
        .collect(Collectors.toList());

结果为:[6, 7, 2, 6]

通过map方法可以完成映射,该例子完成中 String -> Integer 的映射。

flatMap 流转换

将一个流中的每个值都转换为另一个流,然后把所有流连接成一个流.

List<String> wordList = Arrays.asList("Java 8", "Lambdas", "In", "Action");

List<String> strList = wordList.stream()
        .map(w -> w.split(" "))
        .flatMap(Arrays::stream)
        .distinct()
        .collect(Collectors.toList());

结果为:[Java, 8, Lambdas, In, Action]

map(w -> w.split(" ")) 的返回值为 Stream<String[]>,想获取 Stream,可以通过flatMap方法完成 Stream ->Stream 的转换。

allMatch 匹配所有元素
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);

if (integerList.stream().allMatch(i -> i > 3)) {
    System.out.println("所有元素值都大于3");
} else {
    System.out.println("并非所有元素值都大于3");
}

anyMatch匹配其中一个
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);

if (integerList.stream().anyMatch(i -> i > 3)) {
    System.out.println("存在值大于3的元素");
} else {
    System.out.println("不存在值大于3的元素");
}

noneMatch全部不匹配
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);


if (integerList.stream().noneMatch(i -> i > 3)) {
    System.out.println("值都小于3的元素");
} else {
    System.out.println("值不都小于3的元素");
}

终端操作实例

count 统计流中元素个数
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Long result = integerList.stream().count();

结果为:5

findFirst 查找第一个
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);

Optional<Integer> result = integerList.stream().filter(i -> i > 3).findFirst();

System.out.println(result.orElse(-1));

结果为:4

findAny 随机查找一个
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);

Optional<Integer> result = integerList.stream().filter(i -> i > 3).findAny();

System.out.println(result.orElse(-1));

结果为:4
提供findAny方法是为了更好的利用并行流,findFirst方法在并行上限制更多

reduce 将流中的元素组合
用于求和:
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);

int sum = integerList.stream()
        .reduce(0, Integer::sum);

结果为:15
reduce接受两个参数,一个初始值这里是0,一个 BinaryOperatoraccumulator
来将两个元素结合起来产生一个新值,另外reduce方法还有一个没有初始化值的重载方法。

用于获取最大最小值
List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");
Optional<Integer> min = stringList.stream()
        .map(String::length)
        .reduce(Integer::min);

Optional<Integer> max = stringList.stream()
        .map(String::length)
        .reduce(Integer::max);

结果为:Optional[2] 和 Optional[7]

min/max 获取最小最大值

写法一:

List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");

Optional<Integer> min = stringList.stream()
        .map(String::length)
        .min(Integer::compareTo);

Optional<Integer> max = stringList.stream()
        .map(String::length)
        .max(Integer::compareTo);

写法二:

List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");

OptionalInt
        min = stringList.stream()
        .mapToInt(String::length)
        .min();

OptionalInt
        max = stringList.stream()
        .mapToInt(String::length)
        .max();

写法三:

List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");

Optional<Integer> min = stringList.stream()
        .map(String::length)
        .reduce(Integer::min);

Optional<Integer> max = stringList.stream()
        .map(String::length)
        .reduce(Integer::max);

结果为:Optional[2] 和 Optional[7]

sum / summingxxx / reduce 求和

sum(推荐)

List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");

int sum = stringList.stream()
        .mapToInt(String::length)
        .sum();

summingInt

如果数据类型为double、long,则通过summingDouble、summingLong方法进行求和。

List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");

int sum = stringList.stream()
        .collect(summingInt(String::length));

reduce

List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");

int sum = stringList.stream()
        .map(String::length)
        .reduce(0, Integer::sum);

/**
 * 求Integer集合的元素之和、乘积和最大值
 *
 */
    List<Integer> list = Arrays.asList(1, 2, 3, 4);
    //求和
    Optional<Integer> reduce = list.stream().reduce((x,y) -> x+ y);
    System.out.println("求和:"+reduce);
    //求积
    Optional<Integer> reduce2 = list.stream().reduce((x,y) -> x * y);
    System.out.println("求积:"+reduce2);
    //求最大值
    Optional<Integer> reduce3 = list.stream().reduce((x,y) -> x>y?x:y);
    System.out.println("求最大值:"+reduce3);

/*
 * 求所有员工的工资之和和最高工资
 */
    Optional<Integer> reduce = personList.stream().map(Person :: getSalary).reduce(Integer::sum);
    Optional<Integer> reduce2 = personList.stream().map(Person :: getSalary).reduce(Integer::max);
    System.out.println("工资之和:"+reduce);
    System.out.println("最高工资:"+reduce2);

求和、求最大值、最小值的时候,对于相同操作有不同的方法可以选择执行。可以选择collect、reduce、min/max/sum方法,推荐使用min、max、sum方法。因为它最简洁易读,同时通过mapToInt将对象流转换为数值流,避免了装箱和拆箱操作

averagingxxx 求平均值

averagingInt、 averagingLong、 averagingDouble

List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");

double average = stringList.stream()
        .collect(averagingInt(String::length));

summarizingxxx 同时求总和、平均值、最大值、最小值
List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");

IntSummaryStatistics intSummaryStatistics = stringList.stream()
        .collect(summarizingInt(String::length));

double average = intSummaryStatistics.getAverage(); // 获取平均值
int min = intSummaryStatistics.getMin();            // 获取最小值
int max = intSummaryStatistics.getMax();            // 获取最大值
long sum = intSummaryStatistics.getSum();           // 获取总和

如果数据类型为double、long,则通过summarizingDouble、summarizingLong方法

foreach 遍历
List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");

stringList.stream().forEach(System.out::println);

collect 返回集合
List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");

List<Integer> intList = stringList.stream()
        .map(String::length)
        .collect(toList());

Set<Integer> intSet = stringList.stream()
        .map(String::length)
        .collect(toSet());

等价于

List<Integer> intList = new ArrayList<>();
Set<Integer> intSet = new HashSet<>();
for (String item : stringList) {
    intList.add(item.length());
    intSet.add(item.length());
}

通过遍历和返回集合的使用发现流只是把原来的外部迭代放到了内部进行,这也是流的主要特点之一。内部迭代可以减少好多代码量。

joining 拼接流中的元素
List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");

String result = stringList.stream()
        .map(String::toLowerCase)
        .collect(Collectors.joining("-"));

结果为:java 8-lambdas-in-action

groupingBy 分组
List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");

Map<Integer, List<String>> collect = stringList.stream().collect(groupingBy(String::length));

通过嵌套使用groupingBy进行多级分类

List<String> stringList = Arrays.asList("Java 12", "Lambdas", "In", "Action");

Map<Integer, Map<Integer, List<String>>> collect = stringList.stream()
        .collect(groupingBy(
                String::length,
                groupingBy(String::hashCode)
        ));

结果为:
{2={2373=[In]}, 6={1955883606=[Action]}, 7={1611513196=[Lambdas], -155279169=[Java 12]}}

List<String> stringList = Arrays.asList("Java 12", "Lambdas", "In", "Action");

Map<Integer, Map<String, List<String>>> collect = stringList.stream()
        .collect(groupingBy(
                String::length,
                groupingBy(item -> {
                    if (item.length() <= 2) {
                        return "level1";
                    } else if (item.length() <= 6) {
                        return "level2";
                    } else {
                        return "level3";
                    }
                })
        ));

结果为:{2={level1=[In]}, 6={level2=[Action]}, 7={level3=[Java 12, Lambdas]}}

partitioningBy 分区

分区是特殊的分组,它分类依据是true和false,所以返回的结果最多可以分为两组。

List<String> stringList = Arrays.asList("Java 12", "Lambdas", "In", "Action");

Map<Boolean, List<String>> collect = stringList.stream().collect(partitioningBy(String::isEmpty));

结果为:{false=[Java 12, Lambdas, In, Action], true=[]}

List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);

Map<Boolean, List<Integer>> result = integerList.stream().collect(partitioningBy(i -> i < 3));

结果为:{false=[3, 4, 5], true=[1, 2]}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值