千家信息网

java8的Stream特性是什么

发表于:2025-02-02 作者:千家信息网编辑
千家信息网最后更新 2025年02月02日,本篇内容介绍了"java8的Stream特性是什么"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!ja
千家信息网最后更新 2025年02月02日java8的Stream特性是什么

本篇内容介绍了"java8的Stream特性是什么"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

java.util.Stream 表示能应用在一组元素上一次执行的操作序列。Stream 操作分为中间操作或者最终操作两种,最终操作返回一特定类型的计算结果,而中间操作返回Stream本身,这样就可以将多个操作依次串起来。Stream 的创建需要指定一个数据源,比如 java.util.Collection的子类,List或者Set, Map不支持。Stream的操作可以串行stream()执行或者并行parallelStream()执行。

一、简介

java8新添加了一个特性:流Stream。Stream让开发者能够以一种声明的方式处理数据源(集合、数组等),它专注于对数据源进行各种高效的聚合操作(aggregate operation)和大批量数据操作 (bulk data operation)。

Stream API将处理的数据源看做一种Stream(流),Stream(流)在Pipeline(管道)中传输和运算,支持的运算包含筛选、排序、聚合等,当到达终点后便得到最终的处理结果。

几个关键概念

  1. 元素 Stream是一个来自数据源的元素队列,Stream本身并不存储元素。

  2. 数据源(即Stream的来源)包含集合、数组、I/O channel、generator(发生器)等。

  3. 聚合操作 类似SQL中的filter、map、find、match、sorted等操作

  4. 管道运算 Stream在Pipeline中运算后返回Stream对象本身,这样多个操作串联成一个Pipeline,并形成fluent风格的代码。这种方式可以优化操作,如延迟执行(laziness)和短路( short-circuiting)。

  5. 内部迭代 不同于java8以前对集合的遍历方式(外部迭代),Stream API采用访问者模式(Visitor)实现了内部迭代。

  6. 并行运算 Stream API支持串行(stream() )或并行(parallelStream() )的两种操作方式。

Stream API的特点:

  1. Stream API的使用和同样是java8新特性的 lambda表达式 密不可分,可以大大提高编码效率和代码可读性。

  2. Stream API提供串行和并行两种操作,其中并行操作能发挥多核处理器的优势,使用fork/join的方式进行并行操作以提高运行速度。

  3. Stream API进行并行操作无需编写多线程代码即可写出高效的并发程序,且通常可避免多线程代码出错的问题。

二、简单示例

我们来看一个简单的示例,统计整数数组中正数的个数:

  1. 在java8之前:

    public static void main(String[] args)    {          List numbers = Arrays.asList(-1, -2, 0, 4, 5);                long count = 0;                for(Integer number: numbers)        {            if(number > 0)            {                count++;            }        }                System.out.println("Positive count: " + count);    }
  1. 在java8之后:

    public static void main(String[] args)    {          List numbers = Arrays.asList(-1, -2, 0, 4, 5);              long count = numbers.parallelStream().filter(i -> i>0).count();                System.out.println("Positive count: " + count);    }

可以看到,上例中,使用filter()方法对数组进行了过滤,使用count()方法对过滤后的数组进行了大小统计,且使parallelStream()方法为集合创建了并行流,自动采用并行运算提高速度。在更复杂的场景,还可以用forEach()、map()、limit()、sorted()、collect()等方法进行进一步的流运算。

三、典型接口详解

本节以典型场景为例,列出Stream API常用接口的用法,并附上相应代码。
需要说明的是,Stream API中存在很多方法重载,同名方法本文中可能仅列举一个,请读者注意~

3.1 Stream的生成

java8 Stream API支持串行或并行的方式,可以简单看下jdk1.8 Collection接口的源码(注释只截取部分):

    /**     * @return a sequential {@code Stream} over the elements in this collection     * @since 1.8     */    default Stream stream() {        return StreamSupport.stream(spliterator(), false);    }    /**     * @return a possibly parallel {@code Stream} over the elements in this collection     * @since 1.8     */    default Stream parallelStream() {        return StreamSupport.stream(spliterator(), true);    }

可以看出,在集合类的接口(Collection)中,分别用两种方式来生成:

1. 串行流 : stream()
2. 并行流 : parallelStream()

应该注意的是,使用parallelStream()生成并行流后,对集合元素的遍历是无序的。

3.2 forEach()方法

简单看下forEach()方法的源码(注释只截取部分):

    /**     * Performs an action for each element of this stream.     */    void forEach(Consumer action);

forEach()方法的参数为一个Consumer(消费函数,一个函数式接口)对象,forEach()方法用来迭代流中的每一个数据,例如:

    public static void main(String[] args)    {          List numbers = Arrays.asList(-1, -2, 0, 4, 5);                numbers.stream().forEach(n ->  System.out.println("List element: " + n));    }

上例中,对数组的每个元素进行串行遍历,并打印每个元素的值。

ps:
集合的顶层接口Iterable中也投forEach方法,可以直接对数组元素进行遍历:

    public static void main(String[] args)    {          List numbers = Arrays.asList(-1, -2, 0, 4, 5);                numbers.forEach(n ->  System.out.println("List element: " + n));    }

当然用Strem API的好处不仅仅是遍历~~~

3.3 map()方法

简单看下map()方法的源码(注释只截取部分):

    /**     * Returns a stream consisting of the results of applying the given function to the elements of this stream.     * @param  The element type of the new stream     * @param mapper a non-interfering,     *               stateless     *               function to apply to each element     * @return the new stream     */     Stream map(Function mapper);

map()方法的参数为Function(函数式接口)对象,map()方法将流中的所有元素用Function对象进行运算,生成新的流对象(流的元素类型可能改变)。举例如下:

    public static void main(String[] args)    {          List numbers = Arrays.asList(-1, -2, 0, 4, 5);                numbers.stream().map( n -> Math.abs(n)).forEach(n ->  System.out.println("Element abs: " + n));    }

上例中,用map()方法计算了所有数组元素的绝对值并生成了一个新的流,然后再用forEach()遍历打印。

3.4 flatMap()方法

简单看下flatMap()方法的源码(省略注释):

  Stream flatMap(Function> mapper);

显然,跟map()方法不同的是,Function函数的返回值类型是Stream类型,而不是R类型,即Function函数返回一个Stream流,这样flatMap()能够将一个二维的集合映射成一个一维的集合,比map()方法拥有更高的映射深度(此处可能有一点绕,可结合例子理解),作个简单示例如下:

有一个字符串数组:

List list = Arrays.asList("1 2", "3 4", "5 6");

其有三个元素,每个元素有两个数组并用空格隔开,如果每个元素以空格分割成2个元素,并遍历打印这6个元素,

用flatMap()方法如下:

list.stream().flatMap(item -> Arrays.stream(item.split(" "))).forEach(System.out::println);

而用map()方法:

 list.stream().map(item -> Arrays.stream(item.split(" "))).forEach(n ->n.forEach(System.out::println));

可见,用map()方法,返回了一个"流中流",需要在每个Stream元素遍历时,再加一层forEach进行遍历。

3.5 filter()方法

简单看下filter()方法的源码(注释只截取部分):

    /**     * Returns a stream consisting of the elements of this stream that match the given predicate.     *     * 

This is an intermediate operation. * * @param predicate a non-interfering, * stateless * predicate to apply to each element to determine if it should be included * @return the new stream */ Stream filter(Predicate predicate);

filter()方法的参数为Predicate(函数式接口)对象,再lambda表达式的讲解中我们提到过这个接口,一般用它进行过滤。正如第二章中示例:

    public static void main(String[] args)    {          List numbers = Arrays.asList(-1, -2, 0, 4, 5);              long count = numbers.parallelStream().filter(i -> i>0).count();                System.out.println("Positive count: " + count);    }

用filter方法很容易过滤出整数数组中的自然数。

3.6 reduce()方法

reduce操作又称为折叠操作,用于将流中的所有值合成一个。reduce()方法的源码(不提供计算初始值的reduce方法)(省略注释):

Optional reduce(BinaryOperator accumulator);

reduce()方法参数为BinaryOperator类型的累加器(它接受两个类型相同的参数,返回值类型跟参数类型相同),返回一个Optional对象。
 实际上,Stream API中的mapToInt()方法返回的IntStream接口有类似的 average()、count()、sum()等方法就是做reduce操作,类似的还有mapToLong()、mapToDouble() 方法。当然,我们也可以用reduce()方法来自定义reduce操作。例如我们用reduce()方法来进行整数数组求和操作:

    public static void main(String[] args)    {        List numbers = Arrays.asList(-1, -2, 0, -1, 4, 5, 1);                Integer total = numbers.stream().reduce((t, n) -> t + n).get();                System.out.println("Total: " + total);    }

上例中利用reduce()方法结合lambda表达式轻易的实现了数组的求和功能。

3.7 collect()方法

简单看下collect()方法的源码(注释只截取部分):

    /**     * @param  the type of the result     * @param  the intermediate accumulation type of the {@code Collector}     * @param collector the {@code Collector} describing the reduction     * @return the result of the reduction     */     R collect(Collector collector);

collect()方法的参数为一个java.util.stream.Collector类型对象,可以用java.util.stream.Collectors工具类提供的静态方法来生成,Collectors类实现很多的归约操作,如Collectors.toList()、Collectors.toSet()、Collectors.joining()(joining适用于字符串流)等。看一个简单示例:

    public static void main(String[] args)    {          List numbers = Arrays.asList(-1, -2, 0, 4, 5);                List abss = numbers.stream().map( n -> Math.abs(n)).collect(Collectors.toList());                System.out.println("Abs list: " + abss);    }

上例中,用map()方法生成新的流,再用collect()方法返回原数组的绝对值数组。

3.8 summaryStatistics()方法进行数值统计

其实summaryStatistics()方法并不是Stream接口的方法,而是Stream API采用mapToInt()、mapToLong()、mapToDouble()三个方法分别生成IntStream 、LongStream 、DoubleStream 三个接口类型的对象,这个方法的参数分别为3个函数式接口ToIntFunction、ToLongFunction、ToDoubleFunction,使用时可以用lambda表达式计算返回对应的int、long、double类型即可,简单看下这三个方法的源码(省略注释):

    IntStream mapToInt(ToIntFunction mapper);    LongStream mapToLong(ToLongFunction mapper);    DoubleStream mapToDouble(ToDoubleFunction mapper);

IntStream 、LongStream 、DoubleStream 三个接口类型都有一个summaryStatistics()方法,其中,

  1. IntStream 的方法是:

 IntSummaryStatistics summaryStatistics();
  1. LongStream 的方法是:

 LongSummaryStatistics summaryStatistics();
  1. DoubleStream 的方法是:

 DoubleSummaryStatistics summaryStatistics();

在IntSummaryStatistics、LongSummaryStatistics 、DoubleSummaryStatistics 三个接口类型(位于java.util包下)中,都有诸如统计数量、最大值、最小值、求和、平均值等方法(方法名和返回类型可能不同),利用这些方法我们可以方便的进行数值统计。以IntSummaryStatistics工具包 为例:

    public static void main(String[] args)    {        List numbers = Arrays.asList(-1, -2, 0, 4, 5);                IntSummaryStatistics stats = numbers.stream().mapToInt((x) -> x).summaryStatistics();                System.out.println("Max : " + stats.getMax());        System.out.println("Min : " + stats.getMin());        System.out.println("Sum : " + stats.getSum());        System.out.println("Average : " + stats.getAverage());        System.out.println("Count : " + stats.getCount());    }

3.9 其它方法

Stream API还有一些其它的方法,比如:
    limit()    获取指定数量的流
    sorted()   对流进行排序
    distinct()  去重
    skip()    跳过指定数量的元素
    peek()   生成一个包含原Stream的所有元素的新Stream,并指定消费函数
    count()   计算元素数量
    ......

四、注意事项

Stream中的操作从概念上讲分为中间操作和终端操作

要理解中间操作和终端操作的概念,防止埋坑~

14个常用Stream:

1. forEach 循环

@Testpublic void forEach(){    // 你不鸟我,我也不鸟你    List list = Arrays.asList("you", "don't", "bird", "me", ",",                                        "I", "don't", "bird", "you");    // 方式一:JDK1.8之前的循环方式    for (String item: list) {        System.out.println(item);    }    // 方式二:使用Stream的forEach方法    // void forEach(Consumer action)    list.stream().forEach(item -> System.out.println(item));    // 方式三:方式二的简化方式    // 由于方法引用也属于函数式接口,所以方式二Lambda表达式也可以使用方法引用来代替    // 此种方式就是方式一、方式二的简写形式    list.stream().forEach(System.out::println);}

2. filter 过滤

public class User {    private Long id;    private String phone;    private Integer age;    public User(){}    public User(Long id, String username, Integer age) {        this.id = id;        this.username = username;        this.age = age;    }    // Getter & Setter & toString}@Testpublic void filter(){    List users = Arrays.asList(            new User(1L, "mengday", 28),            new User(2L, "guoguo", 18),            new User(3L, "liangliang", 17)    );    // Stream filter(Predicate predicate);    users.stream().filter(user -> user.getAge() > 18).forEach(System.out::println);}

3. map 映射

@Testpublic void map(){    List list = Arrays.asList("how", "are", "you", "how", "old", "are", "you", "?");    //  Stream map(Function mapper);    list.stream().map(item -> item.toUpperCase()).forEach(System.out::println);}

4. flatMap

@Testpublic void flatMap(){    List a = Arrays.asList(1, 2, 3);    List b = Arrays.asList(4, 5, 6);    //  Stream flatMap(Function> mapper)    List> collect = Stream.of(a, b).collect(Collectors.toList());    // [[1, 2, 3], [4, 5, 6]]     System.out.println(collect);    // 将多个集合中的元素合并成一个集合    List mergeList = Stream.of(a, b).flatMap(list -> list.stream()).collect(Collectors.toList());    // [1, 2, 3, 4, 5, 6]    System.out.println(mergeList);    // 通过Builder模式来构建    Stream stream = Stream.builder().add("hello").add("hi").add("byebye").build();}

5. sorted 排序

@Testpublic void sort(){    List list = Arrays.asList("c", "e", "a", "d", "b");    // Stream sorted(Comparator comparator);    // int compare(T o1, T o2);    list.stream().sorted((s1, s2) -> s1.compareTo(s2)).forEach(System.out::println);}

6. distinct 去重复

@Testpublic void distinct(){    // 知之为知之,不知为不知    Stream stream = Stream.of("know", "is", "know", "noknow", "is", "noknow");    stream.distinct().forEach(System.out::println); // know is noknow}

7. count 总数量

@Testpublic void count(){    Stream stream = Stream.of("know", "is", "know", "noknow", "is", "noknow");    long count = stream.count();    System.out.println(count);}

8. min、max

@Testpublic void min(){    List list = Arrays.asList("1", "2", "3", "4", "5");    // Optional min(Comparator comparator);    Optional optional = list.stream().min((a, b) -> a.compareTo(b));    String value = optional.get();    System.out.println(value);}

9. skip、limit

@Testpublic void skip(){    List list = Arrays.asList("a", "b", "c", "d", "e");    // Stream skip(long n)    list.stream().skip(2).forEach(System.out::println);  // c、d、e}@Testpublic void limit(){    List list = Arrays.asList("a", "b", "c", "d", "e");    list.stream().skip(2).limit(2).forEach(System.out::println);    // c、d}

10. collect

@Testpublic void collect(){    List list = Arrays.asList("a", "b", "c", "d", "e");    // Stream -> Collection    List collect = list.stream().collect(Collectors.toList());    // Stream -> Object[]    Object[] objects = list.stream().toArray();}

11. concat

@Testpublic void concat(){    List list = Arrays.asList("a", "b");    List list2 = Arrays.asList("c", "d");    Stream concatStream = Stream.concat(list.stream(), list2.stream());    concatStream.forEach(System.out::println);}

12. anyMatch、allMatch

@Testpublic void match(){    // 你给我站住    List list = Arrays.asList("you", "give", "me", "stop");    // boolean anyMatch(Predicate predicate);    // parallelStream可以并行计算,速度比stream更快    boolean result = list.parallelStream().anyMatch(item -> item.equals("me"));    System.out.println(result);}/*** anyMatch伪代码 * 如果集合中有一个元素满足条件就返回true * @return */public  boolean anyMatch() {    List list = Arrays.asList("you", "give", "me", "stop");    for (String item : list) {        if (item.equals("me")) {            return true;        }    }        return false;    }

13. reduce 归纳

@Testpublic void reduce(){    Stream stream = Stream.of("you", "give", "me", "stop");    // Optional reduce(BinaryOperator accumulator);    Optional optional = stream.reduce((before, after) -> before + "," + after);    optional.ifPresent(System.out::println);    // you,give,me,stop}

BigDecimal求和

public static void main(String[] args) {    List list = Arrays.asList(            new BigDecimal("11.11"),            new BigDecimal("22.22"),            new BigDecimal("33.33")    );    // 66.66    BigDecimal sum = list.stream().reduce(BigDecimal.ZERO, BigDecimal::add);    System.out.println(sum);}

14. findFirst、findAny

@Testpublic void findFirst(){    Stream stream = Stream.of("you", "give", "me", "stop");    String value = stream.findFirst().get();    System.out.println(value);}@Testpublic void findAny(){    Stream stream = Stream.of("you", "give", "me", "stop");    String value2 = stream.findAny().get();    System.out.println(value2);}

"java8的Stream特性是什么"的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注网站,小编将为大家输出更多高质量的实用文章!

0