函数式接口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);
}
}
四、总结
本文地址:https://blog.csdn.net/LuKeliyy/article/details/111915878
下一篇: 淘宝店铺装修时 如何优化细节?