一、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之前,我们需要先了解一个类:OptionalOptinal类是一个可以为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;}@Overridepublic 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) % 10List<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. 将所有玩家级别+5List<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. 累乘:需要将集合类型转换成BigIntegerList<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);//转换成mapList<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类相关推荐

  1. Lambda表达式接口更新方法引用函数式接口Stream流

    Lambda表达式&接口更新&方法引用&函数式接口&Stream流 Lambda 1.程序启动三种不同的表现形式 2.Lambda表达式的标准格式 3.练习 Lambd ...

  2. Java8新特性学习_001_(Lambda表达式,函数式接口,方法引用,Stream类,Optional类)

    目录 ■代码 ■代码运行结果 ■代码说明 ・44行:Stream的.foreach方法ー参数类型:函数式接口 ・82行:Interface中,default方法 ・92行   Stream的.max方 ...

  3. Lambda01 编程范式、lambda表达式与匿名内部类、函数式接口、lambda表达式的写法...

    1 编程范式 主要的编程范式有三种:命令式编程,声明式编程和函数式编程. 1.1 命令式编程 关注计算机执行的步骤,就是告诉计算机先做什么后做什么 1.2 声明式编程 表达程序的执行逻辑,就是告诉计算 ...

  4. java 函数式接口与Lambda表达式

    目录 函数式接口 函数式接口简介 什么是 @FunctionalInterface 内置的函数式接口 Stream和Lambda常用的函数式接口 函数式接口的使用 Lambda表达式 Lambda来源 ...

  5. JDK8新特性之函数式接口

    转载自 JDK8新特性之函数式接口 什么是函数式接口 先来看看传统的创建线程是怎么写的 Thread t1 = new Thread(new Runnable() {@Overridepublic v ...

  6. 【Java】JDK8新特性之函数式接口

    原文:http://www.javastack.cn/article/2017/jdk8-new-feature-functional-interface/ 什么是函数式接口 先来看看传统的创建线程是 ...

  7. java函数式接口意义与场景

    前言 想到记录下这篇主要是两个原因,1. 虽然自己很早就接触了函数式接口,但是基本没有深入探究过使用场景.2. 工作中接触了越来越多场景后,感觉对函数式接口有更多使用需求,能很好的美化自己代码(少写几 ...

  8. 学习笔记之-java8的新特性-函数式接口,lambda表达式,方法引用,Stream API,Optional类

    1.Lambda表达式 用匿名内部类的方法去创建多线程1.new Thread2.参数传递new Runnable3.重写run方法4.在run方法中去设置线程任务5.调用start问题:我们最终目标 ...

  9. 未公开接口主要指以下哪几类_Java8的 Stream 函数式接口,你了解多少?

    点击蓝色"程序职场"关注我哟 加个"星标",天天和你一起进步 作者:litesky www.jianshu.com/p/2338cabc59e1 函数式接口是伴 ...

最新文章

  1. 一个1990年代的小故事
  2. 带你走进rsync的世界
  3. Dijkstra(单源最短路算法)
  4. 计算机课是一体化教学吗,计算机基础课程理实一体化教学模式
  5. STL bitset用法总结
  6. linux-文件类型-七种
  7. Spring Framework--SpringMVC(1)--DispatcherServlet
  8. vaspkit使用_VASP 的光学性质计算及 vaspkit 的安装与使用
  9. 二分--1043 - Triangle Partitioning
  10. 新浪微博 android2.3,BlackLight新浪微博客户端
  11. 如何下载收费歌曲(不用任何软件插件,安全无毒)
  12. HTTP 协议的演变历程
  13. word教程之word2007和2010版本查找和替换快捷键介绍
  14. 图像融合:Image Fusion with Guided Filtering
  15. Hadoop初入门的坑
  16. U3V实现——CYUSB3014之GPIF总结
  17. 为什么大学生活这么充实(累)
  18. 【导数术】6.端点效应
  19. ASP.NET Core开发-后台任务利器Hangfire使用
  20. 颜色的前世今生12·RGB显色系统详解(中)

热门文章

  1. OPengl实现小球围绕大球旋转的效果
  2. java课程设计斗地主_Java课程设计---web版斗地主
  3. 人工智能中的常用搜索策略
  4. 深度学习遥感图像分类常用数据集简介以及下载地址
  5. QT遇到资源文件不显示的问题这样解决
  6. 时间控件-时分秒/分秒/年月日
  7. 锂电池升压到5V1A,PW5300设计布局
  8. php怎么统计流量,PHP学习笔记:php网络流量统计系统
  9. 技术人员如何创业《四》—— 打造超强执行力团队
  10. 字符串以.作为split()的分割符