欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

函数式接口Stream类

程序员文章站 2022-03-19 15:59:02
一、Stream概述1.1 关于流的简介 Java8是一个非常成功的版本,新增的Stream,配合同版本出现的Lambda,为我们操作集合提供了极大的便利。​ Stream 是 Java8 中处理集合的关键抽象概念,它可以指定我们希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来执行并行操作。​ Stream将要处理的元素集合看做一种流,在流的过程中,借助...

一、Stream概述

1.1 关于流的简介

  Java8是一个非常成功的版本,新增的Stream,配合同版本出现的Lambda,为我们操作集合提供了极大的便利。
​  Stream 是 Java8 中处理集合的关键抽象概念,它可以指定我们希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来执行并行操作。
​  Stream将要处理的元素集合看做一种流,在流的过程中,借助Stream API对流中元素进行操作,如筛选、排序、聚合等。

1.2 流的特点

​	1、Stream不存储数据,而是按照特定的规则对数据进行计算
​	2、Stream不会改变数据源,通常情况下会产生一个新的集合或一个值(peek方法可以修改流中的元素)
​	3、惰性求值,流在中间处理过程中,只是对操作进行了记录,并不会立即执行,需要等到执行终止操作时才会进行实际运算

1.3 流操作的分类

函数式接口Stream类

​	Stream可以由数组或集合创建,对流的操作分为两种:
​	1、中间操作:每次返回一个新的流,可以有多个
​		  无状态:指元素的处理不受之前元素的影响
​		  有状态:指该操作只有获取到所有元素后才能继续下去
​	2、终端操作:每个流只能进行一次终端操作,终端操作后,流无法再次使用
​		  终端操作会产生一个新的集合或值
​		  非短路操作:指必须处理所有元素才能获取到最终结果
​		  短路操作:指遇到某些符合条件的元素就可以得到最终结果,如A || B,A为True时,无需判断B的结果

二、Stream的创建

Stream可以通过集合或数组创建

2.1 通过集合的stream()方法创建

List<String> list = Arrays.asList("疾风剑豪", "迪莫", "盲僧", "德莱文");
//创建一个顺序流
Stream<String> stream = list.stream();
//创建一个并行流
Strean<String> parallelStream = list.parallelStream();

2.2 通过Arrays.steam()方法创建

int[] array = {1234, 1235, 1236};
IntStream stream = Arrays.stream();

2.3 通过Stream的静态方法创建

Stream<Integer> stream1 = Stream.of(1, 2, 3, 4, 5);
//生成从1开始的5个奇数
Stream<Integer> stream2 = Stream.iterate(1, num -> num + 2).limit(5);
Stream<Double> stream3 = Stream.generate(Math::random).limit(3);                           
Random random = new Random(System.currentTimeMillis());
Stream<Integer> stream4 = Stream.generate(()->random.nextInt(1000));

2.4 stream和parallelStream的简单区分

​ stream是顺序流,由主线程按顺序对流执行操作,而parallelStream是并行流,内部以多线程并行执行的方式对流进行操作
​ 并行操作的前提是流中的数据处理没有顺序的要求
​ 下面以筛选集合中的奇数为例,说明一下两者处理的不同之处:
​ 如果流中的数据量足够大,并行流可以加快处理速度
​ 除了直接创建并行流,还可以通过parallel()方法将顺序流转换成并行流:

int[] array = {1234, 1235, 1236};
IntStream stream = Arrays.stream(array);
Stream stream2 = stream.parallel();

三、Stream的使用

3.1 Optinal类

在使用Stream之前,我们需要先了解一个类:Optional
  Optinal类是一个可以为null的容器对象。
  如果容器中的值存在,则isPresent()方法返回true,调用get()方法就可以返回容器中的对象
  Optional提供了很多有用的方法,这样我们就不需要显式进行空值检测
  Optional类的引入很好地解决了空指针异常

3.1.1 Optinal类中的方法

函数式接口Stream类

3.1.2 使用示例

public class Demo2 {
    public static void main(String[] args) {
        //创建元素对象
        Integer value1 = null;
        Integer value2 = new Integer(1024);
        //创建一个允许为null值的Optional容器对象
        Optional optional1 = Optional.ofNullable(value1);
        //创建一个不允许null值的Optional对象
        Optional optional2 = Optional.of(value2);
        ShowInfo(optional1, "optional1");
        ShowInfo(optional2, "optional2");
    }
    /** 打印指定容器对象的信息 */
    private static void ShowInfo(Optional optional, String optionalName){
        System.out.println(optionalName + "中的值是否存在:" + optional.isPresent());
        System.out.println(optionalName + "中的值不在就返回默认值:" + optional.orElse(0));
        try {
            System.out.println(optionalName + "中的值为:" + optional.get());
        } catch (RuntimeException e) {
            System.out.println("异常信息:" + e);
        }
    }
}

3.2 遍历/匹配(foreach/filter/match)

Stream支持类似集合的便利和匹配操作,只是Stream中的元素是以Optional类型存在的

public class Demo3 {
    public static void main(String[] args) {
        List<Integer> intList = RandomUtil.generateRandomList(0, 300, 20);
        System.out.println("生成的随机集合:");
        ShowList(intList);
        //1. 筛选(filter)  筛选出集合中所有的偶数元素
        List<Integer> intList1 = intList.stream().filter(num -> num % 2 == 0).collect(Collectors.toList());
        System.out.println("筛选出所有偶数元素的集合:");
        ShowList(intList1);
        System.out.println("返回小于100的任意元素:(使用场景:并行流)" +
                intList.stream().filter(num -> num < 100).findAny().orElse(-1));
        System.out.println("返回小于100的第一个元素:" +
                intList.stream().filter(num -> num < 100).findFirst().orElse(-1));


        //2. 匹配(match)
        System.out.println("集合中所有元素都大于200:" + intList.stream().allMatch(num -> num > 200));
        System.out.println("集合中有没有元素大于200:" + intList.stream().anyMatch(num -> num > 200));
    }

    private static void ShowList(List<Integer> intList) {
        StringBuilder str = new StringBuilder();
        intList.forEach(num -> {
            str.append(num).append(", ");
        });
        str.setLength(str.toString().length() - 2);
        System.out.println(str);
    }
}

3.3 筛选

筛选是按照一定的规则 校验流中的元素,将符合条件的元素提取到新流中的操作
​示例:筛选出武力值大于1100、等级<5 的玩家,返回到新的集合中

import java.util.LinkedList;
import java.util.List;

public class Player implements Cloneable{
    public static List<Player> playerList;
    static {
       initPlayers();
    }
    public static List<Player> initPlayers(){
        playerList = new LinkedList<>();
        playerList.add(new Player("盲僧", 5, "男", 1124L, "至尊王权"));
        playerList.add(new Player("迪莫", 5, "女", 1104L, "至尊王权"));
        playerList.add(new Player("张飞", 5, "男", 1024L, "自然意志"));
        playerList.add(new Player("王昭君", 5, "女", 1004L, "自然意志"));
        playerList.add(new Player("德莱文", 5, "女", 1114L, "自然意志"));
        playerList.add(new Player("蒸汽机器人", 5, "女", 1000L, "自然意志"));
        return playerList;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    private String nickName;
    private Integer level;
    private String sex;
    private Long forceValue;        //武力值
    private String serverArea;      //所属大区

    public Player(){}

    public Player(String nickName, Integer level, String sex, Long forceValue, String serverArea) {
        this.nickName = nickName;
        this.level = level;
        this.sex = sex;
        this.forceValue = forceValue;
        this.serverArea = serverArea;
    }

    // 省略 getters / setters / toString ...
}
public class Demo4 {
    public static void main(String[] args) {
        List<Player> playerList = Player.initPlayers();
        //1. 筛选出武力值大于1100的玩家
        List<Player> playerList1 = playerList.stream()
                .filter(player -> player.getForceValue() > 1100)
                .collect(Collectors.toList());
        System.out.println("武力值大于1100的玩家");
        playerList1.forEach(System.out::println);
        //2. 筛选出等级在5级以下的玩家
        List<Player> playerList2 = playerList.stream()
                .filter(player -> player.getLevel() <= 5)
                .collect(Collectors.toList());
        System.out.println("等级在5级以下的玩家");
        playerList2.forEach(System.out::println);
    }
}

3.4 聚合(min/max/count)

public class Demo5 {
    public static void main(String[] args) {
        List<String> strList = Arrays.asList("一览众山小", "只有敬亭山", "晚风拂柳笛声残", "草盛豆苗稀", "种豆南山下");

        //1. 获取字符串集合中最长的元素
        String maxLengthStr1 = strList.stream().max((s1, s2) -> s1.length() - s2.length()).get();
        //2. 按照自定义顺序,获取排在最前面的元素(与1写法类似)
        String str3 = strList.stream().max(String::compareTo).get();
        System.out.println("按照字符串顺序排序的第一个字符串:" + str3);
        //3. 使用Comparator的comapring方法
        String maxLengthStr2 = strList.stream().max(Comparator.comparing(String::length)).get();
        System.out.println(maxLengthStr1 + "\t" + maxLengthStr2);
        //4. 返回流中元素的总数
        long count  = strList.stream().filter(str->str.contains("山")).count();
        System.out.println("包含“山”的元素个数:" + count);
        //5. 自定义集合排序中的极值
        List<Player> playerList = Player.initPlayers();
        Player minLevelPlayer = playerList.stream().min(Comparator.comparing(Player::getLevel)).get();
        System.out.println("等级最低的玩家:" + minLevelPlayer);
        //6. 等级在5级以上的玩家总数
        long count1 = playerList.stream().filter(player -> player.getLevel() > 5).count();
        System.out.println("等级在5级以上的玩家总数:" + count1);
    }
}

3.5 映射(map/flatMap)

​	映射可以将一个流的元素按照一定的规则映射到另一个流中
​	map:接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新元素
​	可以抽取列表元素的某个属性形成新的列表,但是无法对更深层的属性做提取;map能够直接操作list中的每个对象
	flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
	可以操作更深层的数据

3.5.1 map映射

public class Demo6 {
    public static void main(String[] args) {
        //1. 将集合中的所有字符串进行大小写转换
        String[] strArray = {"fairy", "sinsoledad", "kilig", "redamancy"};
        List<String> upperList1 = Stream.of(strArray)
                .map(str -> str.toUpperCase()).collect(Collectors.toList());
        List<String> upperList2 = Arrays.stream(strArray)
                .map(String::toUpperCase).collect(Collectors.toList());
        System.out.println("大小写转换的结果:");
        System.out.println(upperList1);
        System.out.println(upperList2);
        //2. 返回一个新集合:(每个元素 + 5) % 10
        List<Integer> intList = RandomUtil.generateRandomList(0, 9, 10);
        System.out.println("加密前:" + intList);
        List<Integer> newIntList = intList.stream()
                .map(num -> (num + 5) % 10).collect(Collectors.toList());
        System.out.println("加密后:" + newIntList);
        intList = newIntList.stream()
                .map(num -> (num + 5) % 10).collect(Collectors.toList());
        System.out.println("解密后:" + intList);

        //3. 将所有玩家级别+5
        List<Player> playerList = Player.initPlayers();
        System.out.println("源集合:");
        playerList.forEach(System.out::println);
        //在映射时,有可能会修改源集合中的数据
        playerList.stream().map(player -> {
            player.setLevel(player.getLevel() + 5);
            return player;
        }).collect(Collectors.toList());
        System.out.println("修改后:");
        playerList.forEach(System.out::println);
        //不修改的方式
        playerList = Player.initPlayers();
        List<Player> updatedList = playerList.stream().map(player -> {
            try {
                player = (Player) player.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            player.setLevel(player.getLevel() + 5);
            return player;
        }).collect(Collectors.toList());
        System.out.println("修改后的集合:");
        updatedList.forEach(System.out::println);
        System.out.println("源集合:");
        playerList.forEach(System.out::println); 
    }
}

3.5.2 flatMap映射

​	可以操作更深层的数据
​	理解什么是扁平化,考虑一个像[[1,2,3],[4,5,6],[7,8,9]]这样的具有“两个层次”的结构。
​	扁平化意味着将其转化为“一个一级”结构:[1,2,3,4,5,6,7,8,9]。
​	可以把flatMap的作用想象成,把一个流中的每一个值都转换成另一个流,然后再把这些流合并起来做操作,有点总-分-总的样子
public class Demo7 {
    public static void main(String[] args) {
        //1. 将一个包含多个字符串元素的数组扁平化
        List<String> list = Arrays.asList("第一个元素", "第二个元素", "第三个元素", "第四个元素");
        List<String> flatList = list.stream().flatMap(str -> {
            //将每个元素都转换成一个流对象
            String[] strArray = str.split("");
            return Arrays.stream(strArray);
        }).collect(Collectors.toList());
        System.out.println("扁平化后的集合:");
        System.out.println(flatList);

        //2. 返回字符串数组中非重复的字符
        String[] strArray = {"fairy", "sinsoledad", "kilig", "redamancy"};
        List<String> resultList1 = Stream.of(strArray)
                .flatMap(str -> Arrays.stream(str.split("")))
                .distinct().collect(Collectors.toList());
        List<String> resultList2 = Stream.of(strArray)
                .map(str->str.split(""))
                .flatMap(strings -> Arrays.stream(strings))
                .distinct().collect(Collectors.toList());
        System.out.println("去重后的结果:" + resultList1);
        System.out.println("去重后的结果:" + resultList2);
    }
}

3.5.3 peek()操作

​	peek()操作接收的是一个Consumer函数,即会按照Consumer函数提供的逻辑去消费流中的每个元素
​	与map()的作用相似,最大的区别就是传入的函数有没有返回值
​	peek接收一个Consumer,而map接收一个Function。
​	Consumer是没有返回值的,它只是对Stream中的元素进行某些操作,但是操作之后的数据并不返回到Stream中,所以Stream中的元素还是原来的元素。
​	而Function是有返回值的,这意味着对于Stream的元素的所有操作都会作为新的结果返回到Stream中。
​	这就是为什么peek String不会发生变化而peek Object会发生变化的原因。
		//使用peek方法作映射
        playerList = Player.initPlayers();
        System.out.println("修改前的源集合:");
        playerList.forEach(System.out::println);
        List<Player> newPlayerList =  playerList.stream().
                peek(player -> player.setLevel(player.getLevel() + 10)).collect(Collectors.toList());
        System.out.println("修改后的源集合:" + playerList);
        playerList.forEach(System.out::println);

3.6 规约(reduce)

规约也称缩减,是把一个流缩减成一个值。可以实现对集合的求和、求乘积或求最值操作

//第一次执行时,accoumulator函数的第一个参数为流中第一个元素,第二个参数为流中第二个元素;
//第二次执行时,第一个参数为第一次函数执行的结果,第二个参数为流中第三个元素,依此类推...
Optional<T> reduce(BinaryOperator<T> accumulator)
//流程与上面类似,只是第一次执行时,accumulator函数的第一个参数为identity,第二个参数为流中第一个元素
T reduce(T identity, BinaryOperator<T> accumulator)
//1. 在串行流(stream)中,该方法跟第二个方法一样,即第三个参数combiner不会起作用
//2. 在并行流(parallelStream)中,每个线程的执行流程就跟第二个方法reduce(identity,accumulator)一样
//而第三个参数combiner函数,则是将每个线程的执行结果当成一个新的流,然后使用第一个方法reduce(accumulator)流程进行规约
<U> U reduce(U identity,BiFunction<U, ? super T, U> accumulator,BinaryOperator<U> combiner)

代码示例

public class Demo8 {
    public static int Sum(int num1, int num2){
        return num1 + num2;
    }

    public static void main(String[] args) {
        List<Integer> intList = RandomUtil.iterateList(1, 101);
        //1. 求和操作
        int sum1 = intList.stream().reduce((num1, num2) -> num1 + num2).get();
        int sum2 = intList.stream().reduce(Integer::sum).get();
        int sum3 = intList.stream().reduce(Demo8::Sum).get();
        //参数1为累加器的初始值
        int sum4 = intList.stream().reduce(0, Integer::sum);
        System.out.println("sum1 = " + sum1);
        System.out.println("sum2 = " + sum2);
        System.out.println("sum3 = " + sum3);
        System.out.println("sum4 = " + sum4);

        //2. 累乘:需要将集合类型转换成BigInteger
        List<BigInteger> bigIntegers = intList.stream()
                .map(BigInteger::valueOf).collect(Collectors.toList());
        BigInteger result1 = bigIntegers.stream().reduce((num1, num2) -> num1.multiply(num2)).get();
        System.out.println("元素乘积为:" + result1);

        //3. 求极值
        int max = intList.stream().reduce(Integer::max).get();
        int min = intList.stream().reduce(Integer::min).get();
        System.out.println("最大值/最小值分别为:" + max + ", " + min);

        //4. 求所有5级以上玩家的战力总和
        List<Player> playerList = Player.initPlayers();
        Long totalForce = playerList.stream()
                .filter(player -> player.getLevel()>5)
                .map(Player::getForceValue)             //映射成Long型流,方便reduce
                .reduce(Long::sum).get();
        System.out.println("5级以上玩家的总战力:" + totalForce);

        System.out.println("----------------  原理分割线  ---------------------");

        int sum5 = intList.stream().reduce(0,
                (num1, num2) -> {
                    System.out.println("串行流的累加:" + num1 + ", " + num2);
                    return num1 + num2;
                },
                (num1, num2) ->{
                    System.out.println("在串行流中不起作用");
                    return num1 + num2;
                });
        System.out.println("sum5 = " + sum5);
        System.out.println();
        int sum6 = intList.parallelStream().reduce(0,
                (num1, num2) -> {
                    System.out.println(Thread.currentThread().getName() +
                            "并行流的累加:" + num1 + ", " + num2);
                    return num1 + num2;
                },
                (num1, num2) ->{
                    System.out.println("第三个参数:" + Thread.currentThread().getName() + num1 + ", " + num2);
                    return num1 + num2;
                });
        System.out.println("sum6 = " + sum6);
    }
}

parallelStream默认使用了fork-join框架,其默认线程数是CPU核心数

System.out.println("可用CPU处理器数量:" + Runtime.getRuntime().availableProcessors());
System.out.println("parallelStream默认并发线程数:" + ForkJoinPool.getCommonPoolParallelism());
//问题:为什么parallelStream默认的并发线程数比CPU处理器的数量少1个?
//	   因为最优的策略是每个CPU处理器分配一个线程,然而主线程也算一个线程,所以要占一个名额
//问题:如果电脑性能较差,只有1个CPU会怎样?
//	   默认的并发数就是1,不能为零

通过查阅资料,发现有两种方法来修改默认的多线程数量:

1、全局设置:在运行代码之前,加入如下设置:

System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "64");
//注意:默认并发数不能反复修改!!
//     因为java.util.concurrent.ForkJoinPool.common.parallelism的类型为final,JVM中只允许设置一次

一般不建议修改,因为修改虽然改进当前的业务逻辑,但对于整个项目中其它地方只是用来做非耗时的并行流运算,性能就不友好了,因为所有使用并行流parallerStream的地方都是使用同一个Fork-Join线程池,而Fork-Join线程数默认仅为cpu的核心数。最好是自己创建一个Fork-Join线程池来用,即下面的方法2

2、使用自己创建的线程池 - 可以很方便地进行不同数量线程的并发测试

ForkJoinPool myPool = new ForkJoinPool(108);myPool.submit(() -> list.parallelStream().forEach(/* Do Something */);).get();

使用parallelStream需要注意的地方:

1.parallelStream 适用的场景是CPU密集型的,假如本身电脑CPU的负载很大,那还到处用并行流,并不能起到作用,切记不要在paralelSreram操作中使用IO流

2.不要在多线程中使用parallelStream,原因同上类似,大家都抢CPU并不会提升效率,反而还会加大线程切换的开销

3.7 收集(collect)

​	collect从字面上理解,就是把一个流收集起来,最终可以收集成一个值,也可以收集成一个新的集合
​	collect主要依赖java.util.Stream.Collectors类内置的静态方法

3.7.1 归集(toList() / toSet() / toMap())

由于流并不存储数据,那么在流中的数据完成处理操作后,需要将流中的数据重新归集到新的集合中、toList(), toSet(), 和toMap()是常用方法,此外还有toCollection()、toConcurrentMap()等复杂一些的方法

public class Demo9 {
    public static boolean isEven(int num){
        return num % 2 == 0;
    }
    public String getNickName(){
        return "";
    }
    public static void main(String[] args) {
        List<Integer> intList = RandomUtil.generateRandomList(100, 200, 20);
        System.out.println("原集合:" + intList);
        List<Integer> newList = intList.stream().filter(Demo9::isEven).collect(Collectors.toList());
        Set<Integer> newSet = intList.stream().filter(Demo9::isEven).collect(Collectors.toSet());
        System.out.println(newList);
        System.out.println(newSet);

        //转换成map
        List<Player> playerList = Player.initPlayers();
        Map<String, Player> playerMap = playerList.stream()
                .collect(Collectors.toMap(Player::getNickName, player->player));
        playerMap.forEach((nickName, player) -> {
            System.out.println(nickName + "\t" + player);
        });

    }
}

3.7.2 统计(count / averaging)

Collectors提供了一系列用于数据统计的静态方法
- 计数:count
- 平均值:averagingInt、averagingLong、averagingDouble
- 最值:maxBy、minBy
- 求和:summingInt、summingLong、summingDouble
- 统计以上所有:summarizingInt、summarizingLong、summarizingDouble
public class Demo10 {
    public static void main(String[] args) {
        List<Player> playerList = Player.initPlayers();
        //1. 满足条件的玩家总数
        Long playerCount = playerList.stream()
                .filter(player -> player.getSex().equals("女"))
                .collect(Collectors.counting());
        System.out.println("女性玩家总数:" + playerCount);
        //2. 求平均值
        Double avg = playerList.stream()
                .filter(player -> "男".equals(player.getSex()))
                .collect(Collectors.averagingDouble(Player::getForceValue));
        System.out.println("男性玩家的平均武力值:" + avg);
        //3. 最大值
        Player max = playerList.stream()
                .collect(Collectors.maxBy((player1, player2) ->
                    (int)(player1.getForceValue() - player2.getForceValue())
                )).get();
        System.out.println("最牛逼的玩家是:" + max);
        //4. 一次性统计所有信息
        LongSummaryStatistics forceSummaryStatistics =
                playerList.stream().mapToLong(Player::getForceValue).summaryStatistics();
        System.out.println("最高武力值:" + forceSummaryStatistics.getMax());
        System.out.println("平均武力值:" + forceSummaryStatistics.getAverage());
        System.out.println("玩家总数:" + forceSummaryStatistics.getCount());
    }
}

3.7.3 分区/分组(partitioningBy / groupingBy)

分区:将流按条件分为两个map
​分组:将集合分为多个map

public class Demo11 {
    public static void main(String[] args) {
        List<Player> playerList = Player.initPlayers();
        //1. 按性别分区
        Map<Boolean, List<Player>> partMap = playerList.stream()
                .collect(Collectors.partitioningBy(player -> "女".equals(player.getSex())));
        partMap.forEach((bSex, players) ->{
            String sex = bSex ? "女" : "男";
            System.out.println("--------------" + sex + "性玩家如下---------------");
            players.forEach(System.out::println);
        });

        //2. 按性别分组
        Map<String, List<Player>> groupMap1 = playerList.stream()
                .collect(Collectors.groupingBy(Player::getSex));
        groupMap1.forEach((sex, players) -> {
            System.out.println("--------------" + sex + "性玩家如下---------------");
            players.forEach(System.out::println);
        });

        //3. 先按性别分组,再按服务器大区分组
        Map<String, Map<String, List<Player>>> groupMap2 = playerList.stream()
                .collect(Collectors.groupingBy(Player::getSex, Collectors.groupingBy(Player::getServerArea)));
        groupMap2.forEach((sex, serverMap) ->{
            System.out.println("-------------------  性别分割线  -------------------------");
            serverMap.forEach((serverName, players) -> {
                System.out.println("游戏大区:" + serverName);
                players.forEach(System.out::println);
            });
        });
    }
}

3.7.4 接合(joining)

Collectors.joining()方法以顺序拼接元素,我们可以传递可选的拼接字符串、前缀及后缀

public class Demo12 {
    public static void main(String[] args) {
        //1. 普通字符串数组的拼接   delimiter-分隔符  suffix-后缀 prefix-前缀
        List<String> strList = Arrays.asList("ABCDEFG".split(""));
        String newString = strList.stream()
                .collect(Collectors.joining(" : ", "字符串开始\n", "\n字符串结束"));
        System.out.println(newString);
        //2. 将所有玩家的姓名连接起来
        String playerNames = Player.initPlayers().stream()
                .map(Player::getNickName)
                .collect(Collectors.joining(" - "));
        System.out.println(playerNames);
    }
}

3.7.5 规约(reducing)

public class Demo13 {
    public static void main(String[] args) {
        //计算:1-100之间的平方和
        List<Integer> intList = RandomUtil.iterateList(1, 101);
        Integer sum1 = intList.stream()
                .reduce(0, (num1, num2) -> num1 + num2 * num2);
        Integer sum2 = intList.stream()
                .collect(Collectors.reducing(0, (num1, num2)->num1 + num2*num2));

        System.out.println("sum1 = " + sum1);
        System.out.println("sum2 = " + sum2);
    }
}

3.8 排序(sorted)

sorted为中间操作,分为两种排序:

sorted()	自然排序,流中元素类型需要实现Comparable接口
sorted(Comparator comparator)     使用传入的Comparator比较器自定义排序规则
public class Demo14 {
    public static void main(String[] args) {
        List<Integer> intList = RandomUtil.generateRandomList(100, 1000, 10);
        System.out.println("排序前:" + intList);
        //1. 整型集合排序 - 自然排序
        List<Integer> sortedList1 = intList.stream().sorted().collect(Collectors.toList());
        //2. 整型集合排序 - 使用比较器
        List<Integer> sortedList2 = intList.stream()
                .sorted(Comparator.comparing(Integer::intValue).reversed())
                .collect(Collectors.toList());
        System.out.println("自然排序后:" + sortedList1);
        System.out.println("使用比较器排序后:" + sortedList2);
        //3. 先按大区排序,再按等级排序
        List<Player> playerList = Player.initPlayers().stream()
                .sorted(Comparator
                        .comparing(Player::getServerArea).thenComparing(Player::getLevel))
                .collect(Collectors.toList());
        System.out.println("先按大区排序,再按等级排序");
        playerList.forEach(System.out::println);
    }
}

3.9 合并/跳过/去重/限制(concat / skip / distinct / limit )

public class Demo15 {
    public static void main(String[] args) {
        List<Integer> intList1 = 
            RandomUtil.generateRandomList(100, 200, 10);
        List<Integer> intList2 = 
            RandomUtil.generateRandomList(100, 200, 10);

        //1. 合并两个集合
        List<Integer> intList3 =
                Stream.concat(intList1.stream(), intList2.stream())
                .collect(Collectors.toList());
        System.out.println("合并后:" + intList3);
        //2. 去重
        List<Integer> intList4 = intList3.stream().distinct()
            .collect(Collectors.toList());
        System.out.println("去重后:" + intList4);
        //3. 跳过
        System.out.println("跳过10个元素");
        intList4.stream().skip(10).forEach(System.out::println);
    }
}

四、总结

函数式接口Stream类

本文地址:https://blog.csdn.net/LuKeliyy/article/details/111915878