ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

【Java 8 新特性】之 Stream 流式计算

2021-05-19 20:01:50  阅读:170  来源: 互联网

标签:Java Stream stream 流式 util java public String


简介

Stream 的作用

基于 Lambda,允许以声明性方式处理数据集合,可以把 Stream 流看作是遍历数据集合的一个高级迭代器

使用流的好处:代码以声明性方式书写,说明想要完成什么,而不是说明如何完成一个操作,可以把几个基础操作连接起来,来表达复杂的数据处理的流水线,同时保持代码清晰可读。

引言

Java 8 的 Lambda 让我们可以更加专注于做什么(What),而不是怎么做(How)。

试想一下,如果希望对集合中的元素进行筛选过滤:

  1. 将集合A根据条件一过滤为子集B;

  2. 然后再根据条件二过滤为子集C。

那怎么办?在Java 8 之前的做法可能为:

public class NormalFilter {
  	public static void main(String[] args) {
      	List<String> list = new ArrayList<>();
        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
        list.add("张强");
        list.add("张三丰");
        List<String> zhangList = new ArrayList<>();
        for (String name : list) {
            if (name.startsWith("张")) {
              	zhangList.add(name);
            }
        }
        List<String> shortList = new ArrayList<>();
        for (String name : zhangList) {
            if (name.length() == 3) {
              	shortList.add(name);
            }
        }
        for (String name : shortList) {
          	System.out.println(name);
        }
    }
}

这段代码中含有三个循环,每一个作用不同:

  1. 首先筛选所有姓张的人;

  2. 然后筛选名字有三个字的人;

  3. 最后进行对结果进行打印输出。

每当我们需要对集合中的元素进行操作的时候,总是需要进行循环、循环、再循环。这是理所当然的么?不是循环是做事情的方式,而不是目的。另一方面,使用线性循环就意味着只能遍历一次。如果希望再次遍历,只能再使用另一个循环从头开始。

Stream 的更优写法

public class StreamFilter {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
        list.add("张强");
        list.add("张三丰");
        list.stream() 
          	.filter(s -> s.startsWith("张")) // filter表示过滤,是stream流中的常用方法,后面会细讲。
            .filter(s -> s.length() == 3)
            .forEach(System.out::println);
    }
}

获得 Stream 流的方法

根据 Collection 获取流

前提

首先,java.util.Collection 接口中加入了 default 方法 stream 用来获取流,所以其所有实现类均可获取流。

具体获取方法

import java.util.*;
import java.util.stream.Stream;

public class GetStream {
    public static void main(String[] args) {
        // List
        List<String> list = new ArrayList<>();
        Stream<String> stream1 = list.stream();
        // Set
        Set<String> set = new HashSet<>();
        Stream<String> stream2 = set.stream();
        // Vector
        Vector<String> vector = new Vector<>();
        Stream<String> stream3 = vector.stream();
    }
}

根据 Map 获取流

注意点

java.util.Map 接口不是 Collection 的子接口,且其 K-V 数据结构不符合流元素的单一特征,所以获取对应的流需要分 key 、value 或 entry 等情况。

具体获取方法

import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;

public class GetStream {
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();
        // key
        Stream<String> keyStream = map.keySet().stream();
        // value
        Stream<String> valueStream = map.values().stream();
        // key-value关系
        Stream<Map.Entry<String, String>> entryStream = map.entrySet().stream();
    }
}

根据数组获取流

注意点

如果使用的不是集合或映射而是数组,由于数组对象不可能添加默认方法,所以 Stream 接口中提供了静态方法 of,使用很简单。

具体实现方法

import java.util.stream.Stream;

public class Demo06GetStream {
    public static void main(String[] args) {
        String[] array = { "张无忌", "张翠山", "张三丰", "张一元" };
        Stream<String> stream = Stream.of(array);
    }
}

Stream 常用 API

基础

分类

  • 终结方法:返回值类型不再是 Stream 接口自身类型的方法,因此不再支持类似 StringBuilder 那样的链式调用。本小节中,终结方法包括 count 和 forEach 方法。

  • 非终结方法:返回值类型仍然是 Stream 接口自身类型的方法,因此支持链式调用。(除了终结方法外,其余方法均为非终结方法)

函数拼接与终结方法

在上述介绍的各种方法中,凡是返回值仍然为Stream接口的为函数拼接方法,它们支持链式调用;而返回值不再为 Stream 接口的为终结方法,不再支持链式调用。如下表所示:

forEach : 逐一处理

注意点

虽然方法名字叫 forEach,但是与for循环中的 “for-each” 昵称不同,该方法并不保证元素的逐一消费动作在流中是被有序执行的。

源码

void forEach(Consumer<? super T> action);

示例

import java.util.stream.Stream;

public class StreamForEach {
    public static void main(String[] args) {
        // 获取数组的Stream的方法
        Stream<String> stream = Stream.of("张无忌", "张三丰", "周芷若");
        // 调用ForEach方法
        stream.forEach(s->System.out.println(s));
    }
}

count :统计个数

简介

正如旧集合 Collection 当中的 size 方法一样,流提供 count 方法来数一数其中的元素个数。

long count();

示例

public class StreamCount {
    public static void main(String[] args) {
        Stream<String> original = Stream.of("张无忌", "张三丰", "周芷若");
        Stream<String> result = original.filter(s -> s.startsWith("张"));
        System.out.println(result.count()); // 2
    }
}

filter :过滤

简介

可以通过 filter 方法将一个流转换成另一个子集流。方法声明如下:

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

该接口接收一个 Predicate 函数式接口参数(可以是一个 Lambda 或方法引用)作为筛选条件。

示例

public class StreamFilter {
    public static void main(String[] args) {
        Stream<String> original = Stream.of("张无忌", "张三丰", "周芷若");
        Stream<String> result = original.filter(s -> s.startsWith("张"));
    }
}

在这里通过 Lambda 表达式来指定了筛选的条件:必须姓张。

limit :取用前几个

简介

limit方法可以对流进行截取,只取用前n个。

Stream<T> limit(long maxSize);

示例

import java.util.stream.Stream;

public class StreamLimit {
    public static void main(String[] args) {
        Stream<String> original = Stream.of("张无忌", "张三丰", "周芷若");
        Stream<String> result = original.limit(2);
        System.out.println(result.count()); // 2 
    }
}

skip :跳过前几个

简介

如果希望跳过前几个元素,可以使用 skip 方法获取一个截取之后的新流。

Stream<T> skip(long n);

示例

import java.util.stream.Stream;

public class StreamSkip {
    public static void main(String[] args) {
        Stream<String> original = Stream.of("张无忌", "张三丰", "周芷若");
        Stream<String> result = original.skip(2); //跳过前两个
        System.out.println(result.count()); // 1
    }
}

map :映射

简介

如果需要将流中的元素映射到另一个流中(比如把 int 型转化为 String ),可以用 map。

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

该接口需要一个 Function 函数式接口参数,可以将当前流中的T类型数据转换为另一种R类型的流。

示例

import java.util.stream.Stream;

public class StreamMap {
    public static void main(String[] args) {
        Stream<String> original = Stream.of("10", "12", "18");
        Stream<Integer> result = original.map(s->Integer.parseInt(s)); // 可自动类型推导过程
    }
}

concat :组合

简介

如果有两个流,希望合并成为一个流,那么可以使用 Stream 接口的静态方法 concat。

static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)

示例

import java.util.stream.Stream;

public class StreamConcat {
    public static void main(String[] args) {
        Stream<String> streamA = Stream.of("张无忌");
        Stream<String> streamB = Stream.of("张翠山");
        Stream<String> result = Stream.concat(streamA, streamB);
    }
}

收集 Stream 结果

基础

简介

对流操作完成之后,如果需要将其结果进行收集,例如获取对应的集合、数组等,如何操作?

收集到集合中

简介

Stream 流提供 collect 方法,其参数需要 java.util.stream.Collector<T,A, R> 接口对象来指定收集到哪种集合中。幸运的是 java.util.stream.Collectors 类提供一些方法,可以作为 Collector 接口的实例。

  • public static <T> Collector<T, ?, List<T>> toList():转换为 List 集合。

  • public static <T> Collector<T, ?, Set<T>> toSet():转换为 Set 集合。

示例

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Demo15StreamCollect {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("10", "20", "30", "40", "50");
        List<String> list = stream.collect(Collectors.toList()); // 把结果收集到List中
        Set<String> set = stream.collect(Collectors.toSet()); // 把结果收集到Set中
    }
}

收集到数组中

简介

Stream 提供 toArray 方法来将结果放到一个数组中,由于泛型擦除的原因,返回值类型是 Object[] 的

Object[] toArray();

示例

import java.util.stream.Stream;

public class StreamArray {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("10", "20", "30", "40", "50");
        Object[] objArray = stream.toArray(); // 返回值类型是Object
    }
}

 

 

标签:Java,Stream,stream,流式,util,java,public,String
来源: https://blog.csdn.net/m0_58419935/article/details/117040849

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有