13jdk1.8 Collection新特性Stream流

目标

  • Function函数式接口

  • Predicate函数式接口

  • 流与集合相比的优点

  • 流的延迟执行特点

  • 集合、映射或数组获取流

  • 常用的流操作

  • 流进行并发操作:并行流

  • 流中内容收集到集合中

collect
  • 将流中内容收集到数组中

toArray();

主要内容

  • Predicate接口

  • Function接口

  • Stream流

第1章 常用函数式接口

1.1 Predicate接口

1.1.1 概述

需要对某种类型的数据进行判断,从而得到一个boolean值结果。则可以使用java.util.function.Predicate接口。

1.1.2 抽象方法:test

boolean test(T t)  根据参数t执行判断,返回true或false
  • test方法示例:

    • 定义方法:参数是Predicate接口类型,返回值是boolean类型

    • main方法调用方法判断字符串长度是否大于5

public class PredicateDemo01 {
    public static void main(String[] args){
        // 使用匿名内部类调用
        testPredicate(new Predicate() {
            @Override
            public boolean test(String s) {
                return s.length() > 5;
            }
        });
        // 使用lambda表达式简化
        testPredicate(s -> s.length()>5);
    }
​
    public static void testPredicate(Predicate predicate){
        boolean b = predicate.test("helloworld");
        System.out.println(b);
    }
}

1.1.3 默认方法:and

条件判断,则存在与、或、非三种常见逻辑关系。将两个Predicate条件使用“与”逻辑连接起来实现“and”效果时,可以使用default方法and。其JDK源码为:

default Predicate and(Predicate other) {
    Objects.requireNonNull(other);
    return (t) -> test(t) && other.test(t);
}
  • and方法演示示例

    • 判断一个字符串包含大写“H”,且包含大写“W”

public class PredicateDemo02 {
    public static void main(String[] args) {
        // 定义字符串
        String str = "HelloWorld";
        // 条件1:判断是否包含大写H
        Predicate one = s -> s.contains("H");
        // 条件2:判断是否包含大写W
        Predicate two = s -> s.contains("W");
        // 判断字符串是否同时满足条件1和条件2
        // System.out.println(one.test(str) && two.test(str));
        // 简化版
        boolean b = one.and(two).test(str);
        System.out.println(b);
    }
}

1.1.4 默认方法:or

and的“与”类似,默认方法or实现逻辑关系中的“”。JDK源码为:

default Predicate or(Predicate other) {
    Objects.requireNonNull(other);
    return (t) -> test(t) || other.test(t);
}
  • or 方法演示示例

    • 判断一个字符串包含大写H或者包含大写W”

public class PredicateDemo03 {
    public static void main(String[] args) {
        // 定义字符串
        String str = "helloworld";
        // 条件1:判断是否包含大写H
        Predicate one = s -> s.contains("H");
        // 条件2:判断是否包含大写W
        Predicate two = s -> {
            System.out.println("执行了吗");
            return s.contains("W");
        };
        // 判断字符串是否满足条件1和条件2的其中一个
        boolean  b = one.or(two).test(str);
        System.out.println(b);
    }
}

1.3.5 默认方法:negate

默认方法negate的JDK源代码为:

default Predicate negate() {
    return (t) -> !test(t);
}

negate ! : 执行test方法之后,对结果boolean值进行“!”取反。要在test方法调用之前调用negate方法,

andor方法一样:

import java.util.function.Predicate;
​
public class Demo17PredicateNegate {
​
   private static void method(Predicate predicate) {
      boolean veryLong = predicate.negate().test("HelloWorld");
      System.out.println("字符串很长吗:" + veryLong);
   }
​
   public static void main(String[] args) {
      method(s -> s.length() < 5);
   }
​
}

1.3.6 集合信息筛选

1. 需求说明

数组中有多条“姓名+性别”的信息如下,通过Predicate接口的拼装将符合要求的字符串筛选到集合ArrayList

满足两个条件:

  1. 必须为女生;

  2. 姓名为4个字。

public class DemoPredicate {
​
    public static void main(String[] args) {
        String[] array = { "迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男", "赵丽颖,女" };
    }
​
}

2. 代码实现

public class PredicateDemo05 {
    public static void main(String[] args) {
        String[] array = { "迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男", "赵丽颖,女" };
​
        // 条件1:必须是女生
        // boolean test(String t)
        Predicate one = s -> s.split(",")[1].equals("女");
        // 条件2:姓名为4个字
        Predicate two = s -> s.split(",")[0].length() == 4;
​
        // 遍历数组
        for (String str : array) {
            // 判断str是否同时满足条件1和条件2
            if(one.and(two).test(str)){
                System.out.println(str);
            }
        }
    }
}

1.2 Function接口

1.2.1 概述

java.util.function.Function`接口用于根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件。有进有出,所以称为“函数Function”。

1.2.2 抽象方法:apply

R apply(T t)   将参数t的类型从T类型转换为R类型。
  • apply方法示例:将String类型转换为Integer类型。

public class FunctionDemo01 {
    public static void main(String[] args){
        // 字符串数字
        String str = "100";
        // 将字符串数字转换为整型数字
        Function f = new Function() {
            @Override
            public Integer apply(String s) {
                return Integer.parseInt(s);
            }
        };
        int num = f.apply(str);
        System.out.println(num);
​
        // 使用lambda简化匿名内部类
        // Integer apply(String t)
        Function f1 = s -> Integer.parseInt(s);
        System.out.println(f1.apply("200"));
​
        // 使用方法引用简化lambda表达式
        Function f2 = Integer::parseInt;
        System.out.println(f2.apply("300"));
​
    }
}

1.2.3 默认方法:andThen

Function接口中有一个默认的andThen方法,用来进行组合操作。JDK源代码如:

default  Function andThen(Function after) {
    Objects.requireNonNull(after);
    return (T t) -> after.apply(apply(t));
}
  • andThen方法示例:第一个操作是将字符串解析成为int数字,第二个操作是乘以10。两个操作按照前后顺序组合到一起。

public class FunctionDemo02 {
    public static void main(String[] args){
        // 字符串
        String str = "250";
        // 操作1:将字符串转换基本数据类型:"250" ==> 250
        Function one = Integer::parseInt;
        // 操作2:将操作1执行的结果乘以10
        Function two = num -> num * 10;
​
        // 执行操作1和操作2
       /* int num = one.apply(str);
        int result = two.apply(num);*/
​
        // 执行操作1和操作2:将前一个操作执行的结果作为后一个操作的参数
        int result = one.andThen(two).apply(str);
        System.out.println(result);
    }
}

1.2.4 自定义函数模型拼接

1. 需求说明

使用Function进行函数模型拼接,按照顺序需要执行的多个函数操作为:

  1. 将字符串赵丽颖,20截取数字年龄部分,得到字符串;

  2. 将上一步的字符串转换成为int类型的数字;

  3. 将上一步的int数字累加100,得到结果int数字。

2. 代码实现

public class FunctionDemo03 {
    public static void main(String[] args){
        // 字符串
        String str = "赵丽颖,20";
​
        // 操作1:"赵丽颖,20" ==> "20"
        Function one = s -> s.split(",")[1];
        // 操作2:"20" ==> 20
        Function two = Integer::parseInt;
        // 操作3:20 ==> 120
        Function three = num ->{
            System.out.println("执行了吗");
            return  num + 100;
        };
​
        // 按顺序执行三个操作
        int result = one.andThen(two).andThen(three).apply(str);
​
        // int result = three.compose(two).compose(one).apply(str);
        System.out.println(result);
    }
}

1.3 总结:延迟方法与终结方法

常用函数式接口中,方法可分为两种:

  • 延迟方法:在拼接Lambda函数模型的方法,并不立即执行得到结果。

  • 终结方法:根据拼好的Lambda函数模型,立即执行得到结果值的方法。

通常情况下,常用的函数式接口中唯一的抽象方法为终结方法,而默认方法为延迟方法。但这并不是绝对的。

表格中进行方法分类的整理:

接口名称 方法名称 抽象/默认 延迟/终结
Supplier get 抽象 终结
Consumer accept 抽象 终结
  andThen 默认 延迟
Predicate test 抽象 终结
  and 默认 延迟
  or 默认 延迟
  negate 默认 延迟
Function apply 抽象 终结
  andThen 默认 延迟
  compose 默认 延迟

备注:JDK中更多内置的常用函数式接口,请参考java.util.function包的API文档。

第2章 Stream流

1.1 引言

​ JDK1.8更新最大的亮点除lamada表达式之外,还有极其惊艳的Stream。但Stream不是IO里面的OutputStream或InputStream之类,而是对java中Collection类功能的增强。Stream API配合lamada表达式,极大的提升了编程效率和程序可读性。Stream中支持并行和串行两种方式。

1.2 Stream流引入案例

  • 有如下集合

List list = new ArrayList<>(); 
list.add("张无忌");
list.add("周芷若");
list.add("赵敏"); 
list.add("张强"); 
list.add("张三丰");
  • 按要求执行以下三个操作

    • 首先筛选所有姓张的人。

    • 然后筛选名字有三个字的人。

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

1.2.1 JDK1.8之前的实现方式

public class StreamDemo01 {
    public static void main(String[] args){
        List list = new ArrayList<>();
        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
        list.add("张强");
        list.add("张三丰");
​
        List oneList = new ArrayList<>();
        // 使用jdk1.8之前的方式实现
        for (String str : list) {
            // 判断是否是姓张的
            if(str.startsWith("张")){
                oneList.add(str);
            }
        }
​
        List twoList = new ArrayList<>();
        for (String name : oneList) {
            // 判断是否是三个字
            if (name.length() == 3){
                twoList.add(name);
            }
        }
​
       /* // 打印输出twoList集合的元素
        for (String name : twoList) {
            System.out.println(name);
        }
*/
    }
}

jdk1.8前:

对集合中的元素进行操作的时,总需要对集合进行多次循环。尤其是:使用线性循环时,意味着只能遍历一次。如果希望再次遍历,只能再使用另一个循环从头开始。

jdk1.8后:

Lambda的衍生物Stream可直接遍历独个循环,未使用到的循环遍历可延迟执行.

1.2.2 Stream流的demo

public class StreamDemo02 {
    public static void main(String[] args){
        List list = new ArrayList<>();
        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
        list.add("张强");
        list.add("张三丰");
​
        // 使用Stream流实现
        list.stream()
                .filter(s->{
                    return s.startsWith("张");
                })
                .filter(s->{
                    return s.length() == 3;
                })
                .forEach(System.out::println);
    }
}

直接阅读代码的字面意思即可完美展示无关逻辑方式的语义:获取流、过滤姓张、过滤长度为3、逐一打印。

1.3 获取流的方式

java.util.stream.Stream是Java 8新加入的最常用的流接口。(这并不是一个函数式接口。)

获取一个流

常用方式:

  • 所有的Collection集合都可以通过stream默认方法获取流;

  • Stream接口的静态方法of可以获取数组对应的流。

1.5.1 根据Collection获取流

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

调用集合对象的stream()方法获得Stream对象

1.5.2 根据Map获取流

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

// 获得键对应Stream流对象
Stream keyStream = map.keySet().stream();
// 获得值对应Stream流对象
Stream valueStream = map.values().stream();
// 获得Entry对象对应Stream流对象
Stream> entryStream = map.entrySet().stream();

1.5.3 根据数组获取流

如果使用的不是集合或映射而是数组,由于数组对象不添加默认方法.

所以Stream接口中提供静态方法of:

使用Demo:

 * 通过Stream接口的静态方法of来获得
 // 字符串数组
 String[] strs = {"a"};
 // 获得流关联数组
 Stream arrayStream = Stream.of(strs);

1.5.4 示例代码

public class StreamDemo01 {
    public static void main(String[] args){
        // 创建List集合
        List list = new ArrayList<>();
        // 获得流对象
        Stream stream01 =  list.stream();
​
        // 创建Set集合
        Set set = new HashSet<>();
        Stream stream02 = set.stream();
​
        // 创建Map集合
        Map map = new HashMap<>();
        // 获得键对应Stream流对象
        Stream keyStream = map.keySet().stream();
        // 获得值对应Stream流对象
        Stream valueStream = map.values().stream();
        // 获得Entry对象对应Stream流对象
        Stream> entryStream = map.entrySet().stream();
​
        // 字符串数组
        String[] strs = {"a"};
        // 获得流关联数组
        Stream arrayStream = Stream.of(strs);
        // 直接创建流对象
        Stream a = Stream.of("a", "b");
    }
}

1.4 Stream常用方法

流操作常用方法

分成两种:

  • 终结方法

  • 非终结方法

1.4.1 过滤:filter

1.4.1.1 方法声明

Stream filter(Predicate predicate)
    * 将流中满足条件的元素添加到另一个流中,返回新的流

1.4.1.2 Predicate接口

java.util.stream.Predicate函数式接口,其中唯一的抽象方法为:

boolean test(T t);

该方法将会产生一个boolean值结果,判断指定的条件是否满足。条件结果为true,则Stream流的filter方法将留用元素;条件结果为false,则filter方法将会舍弃元素。

1.4.1.3 基本使用

Stream流中的filter方法基本使用代码如:

public class StreamDemo02 {
    public static void main(String[] args){
        // 创建流对象
        Stream stream01 = Stream.of("abc", "bbxx", "abcde");
        // Predicate函数式接口的抽象方法:boolean test(T t)
        Stream stream02 = stream01.filter(s -> s.length() >= 4);
        // 获得流的元素个数并输出
        System.out.println(stream02.count());
    }
}

1.4.2 统计个数:count

1.4.2.1 方法声明如下

long count()
    * 获得流中元素的个数

1.4.2.2 基本使用

Stream流中的count方法基本使用代码如:

public class StreamDemo02 {
    public static void main(String[] args){
        // 创建流对象
        Stream stream01 = Stream.of("abc", "bbxx", "abcde");
        // Predicate函数式接口的抽象方法:boolean test(T t)
        Stream stream02 = stream01.filter(s -> s.length() >= 4);
        // 获得流的元素个数并输出
        System.out.println(stream02.count());
    }
}

1.4.3 获取前几个:limit

1.4.3.1 方法声明如下

Stream limit(long maxSize)
    * 将流中前maxSize个元素获取到另一个流中
    * maxSize等于零,则会获取一个空流。
    * maxSize大于当前流中元素个数,则会将当前流的所有元素获取到另一个流中
    * maxSize必须大于等于0

1.4.3.2 基本使用

Stream流中的limit方法基本使用代码如:

public class StreamDemo03 {
    public static void main(String[] args){
        // 创建流对象
        Stream stream01 = Stream.of("abc", "dbc", "dde","abe");
       // 获取前2个元素放到另一个流中
        Stream stream02 = stream01.limit(5);
        System.out.println(stream02.count());
    }
}

1.4.4 跳过前几个:skip

1.4.4.1 方法声明如下

Stream skip(long n)
    * 跳过当前流中的前n个元素,将前n个元素后的所有元素添加到另一流中,返回新流。
    * n必须大于等于0
    * n大于当前流中的元素个数,则会产生一个空流。

1.4.4.2 基本使用

public class StreamDemo04 {
    public static void main(String[] args){
        // 创建流对象
        Stream stream01 = Stream.of("abc", "dbc", "dde","abe");
        // 跳过当前流中的前1个元素,
        Stream stream02 = stream01.skip(5);
        System.out.println(stream02.count());
    }
}

1.4.5 映射:map

使用map操作遍历集合中的每个对象,并对遍历结果进行处理,map之后用.collect(Collectors.toList())会得到操作后的集合。方法声明如下

* Stream map(Function mapper)
    * 将当前流中元素的类型从T类型转换为R类型,存储到另一个流中返回

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

1.4.5.1 Function接口

java.util.stream.Function函数式接口,其中唯一的抽象方法为:

R apply(T t);

将一种T类型转换成为R类型,而这转换的过程,则称为“映射”。

1.4.5.2 基本使用

Stream流中的map方法基本使用的代码如:

public class StreamDemo05 {
    public static void main(String[] args){
        // 创建流对象
        Stream stream01 = Stream.of("aaa", "456", "789","12345");
      
        // 将流中的字符串都转换为整型存储到另一个流中
        // Function函数式接口的抽象方法:R apply(T t)
        Stream stream02 = stream01.map(str -> {
            System.out.println(str);
            return Integer.parseInt(str);
        });
        System.out.println(stream02.count());
    }
}

1.4.6 组合:concat

1.4.6.1 方法声明如下

concat方法的声明如下:
    * static  Stream  oncat(Stream a, Stream b)
        * 将流a和流b合并为一个流

1.4.6.2 基本使用

public class StreamDemo06 {
    public static void main(String[] args){
        // 创建流对象
        Stream streamA = Stream.of("aaa", "456", "789","12345");
        Stream streamB = Stream.of("aaa", "456", "789","12345");
​
        // 合并流中元素产生新的流
        Stream streamC = Stream.concat(streamA, streamB);
        // 将流中的元素传递给消费者逐一处理
        streamC.forEach(System.out::println);
    }
}

1.4.7 逐一处理:forEach

1.4.7.1 方法声明如下

 void forEach(Consumer c);
     * 将流中的元素传递给消费者逐一处理

1.4.7.2 基本使用

public class StreamDemo07 {
    public static void main(String[] args){
        // 创建流对象
        Stream streamA = Stream.of("aaa", "456", "789","12345");
        // 将流中的元素传递给消费者逐一处理
        streamA.forEach(System.out::println);
    }
}

方法引用System.out::println便是一个Consumer`函数式接口的实例。

1.5 小结:非终结方法与终结方法

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

方法名 方法作用 方法种类 是否支持链式调用
count 统计个数 终结
forEach 逐一处理 终结
filter 过滤 函数拼接
limit 取用前几个 函数拼接
skip 跳过前几个 函数拼接
map 映射 函数拼接
concat 组合 函数拼接

1.6 案例

  • 案例说明

现在有两个ArrayList集合存储队伍当中的多个成员姓名

两个队伍(集合)的代码如下:

import java.util.ArrayList;
import java.util.List;
​
public class DemoArrayListNames {
​
    public static void main(String[] args) {
        List one = new ArrayList<>();
        one.add("迪丽热巴");
        one.add("宋远桥");
        one.add("苏星河");
        one.add("老子");
        one.add("庄子");
        one.add("孙子");
        one.add("洪七公");
​
        List two = new ArrayList<>();
        two.add("古力娜扎");
        two.add("张无忌");
        two.add("张三丰");
        two.add("赵丽颖");
        two.add("张二狗");
        two.add("张天爱");
        two.add("张三");
        // ....
    }
}

Person类的代码如下

public class Person {
​
    private String name;
​
    public Person() {}
​
    public Person(String name) {
        this.name = name;
    }
​
    @Override
    public String toString() {
        return "Person{name='" + name + "'}";
    }
​
    public String getName() {
        return name;
    }
​
    public void setName(String name) {
        this.name = name;
    }
}

要求如下:

  1. 使用传统的for循环(或增强for循环)依次进行以下若干操作步骤

  2. 使用Stream方式依次进行以下若干操作步骤

1. 第一个队伍只要名字为3个字的成员姓名;
2. 第一个队伍筛选之后只要前3个人;
3. 第二个队伍只要姓张的成员姓名;
4. 第二个队伍筛选之后不要前2个人;
5. 将两个队伍合并为一个队伍;
6. 根据姓名创建Person对象;
7. 打印整个队伍的Person对象信息。

1.6.1 Stream流方式

public class StreamDemo01 {
    public static void main(String[] args) {
        List one = new ArrayList<>();
        one.add("迪丽热巴");
        one.add("宋远桥");
        one.add("苏星河");
        one.add("老子");
        one.add("庄子");
        one.add("孙子");
        one.add("洪七公");
​
        List two = new ArrayList<>();
        two.add("古力娜扎");
        two.add("张无忌");
        two.add("张三丰");
        two.add("赵丽颖");
        two.add("张二狗");
        two.add("张天爱");
        two.add("张三");
        // ....
​
        // 1. 第一个队伍只要名字为3个字的成员姓名;
        // 2. 第一个队伍筛选之后只要前3个人;
        Stream oneStream = one.stream()
                .filter(s -> s.length() == 3)
                .limit(3);
​
        // 3. 第二个队伍只要姓张的成员姓名;
        // 4. 第二个队伍筛选之后不要前2个人;
        Stream twoStream = two.stream()
                .filter(s -> s.startsWith("张"))
                .skip(2);
​
        // 5. 将两个队伍合并为一个队伍;
        // 6. 根据姓名创建Person对象;
        // 7. 打印整个队伍的Person对象信息。
        Stream.concat(oneStream, twoStream)
                .map(Person::new)
                .forEach(System.out::println);
    }
}
​

1.7 收集Stream结果

对流操作完成之后,则需要将其结果进行收集.

1.7.1 收集到集合中

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

public static  Collector> toList():转换为List集合。
public static  Collector> 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 stream = Stream.of("10", "20", "30", "40", "50");
      List list = stream.collect(Collectors.toList());
      Set set = stream.collect(Collectors.toSet());
   }
}

1.7.2 收集到数组中

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

Object[] toArray();

基本使用

import java.util.stream.Stream;
​
public class Demo16StreamArray {
​
   public static void main(String[] args) {
      Stream stream = Stream.of("10", "20", "30", "40", "50");
      Object[] objArray = stream.toArray();
   }
}

解决泛型数组问题

有了Lambda和方法引用之后,可以使用toArray方法的另一种重载形式传递一个IntFunction的函数,继而从外面指定泛型参数。方法声明如下:

 A[] toArray(IntFunction generator);

上例代码中不再局限于Object[]结果,则可以得到String[]结果:

import java.util.stream.Stream;
​
public class Demo17StreamArray {
​
   public static void main(String[] args) {
      Stream stream = Stream.of("10", "20", "30", "40", "50");
      String[] strArray = stream.toArray(String[]::new);
   }
}

既然数组是有构造器的,则传递一个数组的构造器引用即可。

1.8 并发流

以上例子中使用的是串行流,即单线程执行的,其实Stream API中提供有并行流,即多线程执行操作。

java.util.Collection新添加两个默认方法

  • default Stream stream() : 返回串行流

  • default Stream parallelStream() : 返回并行流

stream()parallelStream()方法返回的均是java.util.stream.Stream类型的对象,则说明它们在功能的使用上是没差别的。唯一的差别就是单线程和多线程的执行。

1.8.1 串行流转换为并发流

Stream的父接口java.util.stream.BaseStream中定义一个parallel方法:

S parallel();

在流上调用一下无参数的parallel方法,则当前流即可改变成为支持并发操作的流,返回值仍然为Stream类型。例如:

import java.util.stream.Stream;
​
public class Demo13StreamParallel {
​
    public static void main(String[] args) {
        Stream stream = Stream.of(10, 20, 30, 40, 50).parallel();
    }
​
}

1.8.2 直接获取并发流

在通过集合获取流时,可以直接调用parallelStream方法来直接获取支持并发操作的流。方法定义为:

    default Stream parallelStream() {...}

应用代码为:

import java.util.ArrayList;
import java.util.Collection;
import java.util.stream.Stream;
​
public class Demo13StreamParallel {
​
    public static void main(String[] args) {
        Collection coll = new ArrayList<>();
        Stream stream = coll.parallelStream();
    }
}

1.8.3 并发流基本使用

/**
 * @author pkxing
 * @version 1.0
 * @description 并发流
 * @date 2018/3/5
 */
public class StreamDemo02 {
​
    @Test
    public void testStream() {
        List list = new ArrayList<>();
        // 将1000-1存入list中
        for (int i = 1000; i >= 1; i--) {
            list.add(i);
        }
​
        // 使用串行流:不开线程执行,在当前线程执行
//        List result = list.stream()
//                .map((i)-> {
//                    System.out.println("i = " +Thread.currentThread().getName());
//                    return String.valueOf(i);
//                }).collect(Collectors.toList());
​
        // 使用并发流:开多个线程执行
        List result = list.parallelStream()
                .map((i)-> {
                    System.out.println("i = " +Thread.currentThread().getName());
                    return String.valueOf(i);
                }).collect(Collectors.toList());
​
        System.out.println(result);
    }
}

 

你可能感兴趣的:(javase)