函数式接口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可以由数组或集合创建,对流的操作分为两种:
​	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类中的方法

在这里插入图片描述

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);
    }
}

四、总结

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值