import com.alibaba.fastjson.JSON;
import java.time.LocalDate;
import java.time.Month;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
/**
* 流操作
* Java 流 API
* AutoCloseable
* |
* +--BaseStream
* |
* +--IntStream
* |
* +--LongStream
* |
* +--DoubleStream
* |
* +--Stream<T>
*
* BaseStream 接口定义所有类型的流的所有方法。
* Iterator iterator()
* 终端操作
* 返回流的迭代器。
* sequential()
* 中间操作
* 返回顺序流。 如果流已经是顺序的,则它返回自身。 它将并行流转换为顺序流。
* parallel()
* 中间操作
* 返回并行流。 如果流已经是并行的,则它返回自身。 它将顺序流转换为并行流。
* boolean isParallel()
* 如果流是并行,则返回 true,否则返回 false。
* 在调用终端流操作方法后调用此方法可能会产生不可预测的结果。
* unordered()
* 中间操作
* 返回流的无序版本。 如果流已经是无序的,则它返回自身。
*
*
* 流
* Stream<T> 接口表示元素类型 T 的流。
* Stream 接口包含诸如 filter (),map (),reduce (),collect (),max (),min () 等。
* 当使用原始类型时,我们可以使用三个专门的流接口,称为 IntStream,LongStream 和 DoubleStream。
* 这些接口提供了处理原始值的方法。
* 对于其他基本类型,例如 float,short,byte,我们仍然可以使用三个专用流接口。
*
*/
public class StreamTest {
/**
* Optional 可选项
* java.util.Optional 类来优雅地处理 NullPointerException。
* Optional 是可以包含或不包含非空值的非空值的包装器。
* 可能返回 null 的方法应返回 Optional,而不是 null。
* 如果其包含非空值,则来自可选的 isPresent () 返回 true,否则返回 false。
* 如果 get () 方法包含非空值,则返回非空值,否则抛出 NoSuchElementException。
* 当一个方法返回一个可选,你必须检查它是否包含一个非空值,然后再询问它的值。
* 如果在确保它包含非空值之前调用了 get () 方法,则会抛出 NoSuchElementException,而不是抛出 NullPointerException。
*/
public static void optional(){
Optional<String> empty = Optional.empty();
System.out.println(empty);//Optional.empty
Optional<String> str = Optional.of("www.w3cschool.cn");
System.out.println(str);//Optional[www.w3cschool.cn]
String nullableString = "";
Optional<String> str2 = Optional.of(nullableString);
System.out.println(str2);//Optional[]
//如果 Optional 是空的,这个方法不做任何事情
if (str.isPresent()) {
//获取值 get()
String value = str.get();
System.out.println("Optional contains " + value);//www.w3cschool.cn
//orElse(T defaultValue) 如果 Optional 为空,它返回指定的 defaultValue
System.out.println("Optional defaultValue " + empty.orElse("0"));//"0"
} else {
System.out.println("Optional is empty.");
}
/**
* OptionalInt,OptionalLong 和 OptionalDouble 处理可选的基本值。
* OptionalInt 类中的 getAsInt () 方法返回 int 值。
* getAsLong () 方法从 OptionalLong 类返回 long 值。
* getAsDouble () 方法从 OptionalDouble 类返回 double 值。
*/
}
/**
* 创建流
*/
public static void createStream(){
//空流
Stream<String> stream = Stream.empty();
stream.forEach(System.out::println);
//IntStream,LongStream 和 DoubleStream 接口还包含一个 empty () 静态方法来创建一个空的基本类型流。
//从值创建流
Stream<String> streamOf1 = Stream.of("www.w3cschool.cn");
streamOf1.forEach(System.out::println);//www.w3cschool.cn
Stream<String> streamOf2 = Stream.of("XML", "Java", "CSS", "SQL");
streamOf2.forEach(System.out::println);//XML Java CSS SQL
//对象数组创建流
String[] names = { "XML", "Java", "SQL", "CSS" };
Stream<String> streamArr = Stream.of(names);
streamArr.forEach(System.out::println);
//流构建器
Stream<String> streamBuilder = Stream.<String>builder()
.add("XML")
.add("Java")
.add("CSS")
.add("SQL")
.build();
streamBuilder.forEach(System.out::println);
//IntStream 范围
IntStream oneToFive1 = IntStream.range(1, 6);
oneToFive1.forEach(System.out::println);
IntStream oneToFive2 = IntStream.rangeClosed(1, 5);
oneToFive2.forEach(System.out::println);
//函数流
Stream<Long> tenNaturalNumbers = Stream.iterate(1L, n -> n + 1).limit(10);
tenNaturalNumbers.forEach(System.out::println);
//使用 Math 类的 random 方法作为 Supplier 在 generate () 方法中实现生成流
Stream.generate(Math::random).limit(5).forEach(System.out::println);
//java.util.Random 类提供 ints (),longs () 和 doubles () 分别返回无限 IntStream,LongStream 和 DoubleStream。
new Random().ints().limit(5).forEach(System.out::println);
//来自数组的流
IntStream numbers = Arrays.stream(new int[]{1, 2, 3});
//集合流 Collection 接口包含 stream () 和 parallelStream () 方法,它们分别从 Collection 创建顺序流和并行流。
List<String> list = new ArrayList<>();
list.add("XML");
list.add("Java");
Stream<String> sequentialStream = list.stream();
sequentialStream.forEach(System.out::println);
Stream<String> parallelStream = list.parallelStream();
parallelStream.forEach(System.out::println);
//Java 字符串流 CharSequence 接口的 chars () 返回一个 IntStream,它的元素是表示字符的 int 值。我们可以在 String,StringBuilder 和 StringBuffer 上使用 chars () 方法。
String str = "5 123,123,qwe,1,123, 25";
str.chars().forEach(n -> System.out.print((char)n));
//Regex 流 ava.util.regex.Pattern 类中的 splitAsStream (CharSequence input) 方法返回其元素与模式匹配的 String 流。
//通过使用正则表达式 (“,”) 拆分字符串来获取字符串流
String regexStr = "XML,CSS,HTML";
Pattern.compile(",").splitAsStream(str).forEach(System.out::println);
}
/**
* 操作流
*/
public static void operateStream(){
List<Employee> persons = Employee.persons();
System.out.println("==================count开始====================");
//流计数 Streams 通过 count () 方法支持计数操作,该方法将流中的元素数返回为 long。
System.out.println("Person count: " + persons.stream().count());
System.out.println("==================count结束====================");
System.out.println("==================max开始====================");
//max:返回流中最大值
System.out.println("max count: " + Stream.of(1, 2, 3, 4, 5).max((n1,n2)->n1-n2));
System.out.println("==================max结束====================");
System.out.println("==================min开始====================");
//min:返回流中最小值
System.out.println("min count: " + Stream.of(1, 2, 3, 4, 5).min((n1,n2)->n1-n2));
System.out.println("==================min结束====================");
//peek 返回的流与原始流相同。当原始流中的元素被消费时,会首先调用 peek 方法中指定的 Consumer 实现对元素进行处理.使用 peek () 方法打印通过流管道的元素
System.out.println("==================peek开始====================");
Stream.of(1, 2, 3, 4, 5)
.peek(e -> System.out.println("Taking integer: " + e))
.filter(n -> n % 2 == 1)
.peek(e -> System.out.println("Filtered integer: " + e))
.collect(Collectors.toList());
System.out.println("==================peek结束====================");
System.out.println("==================forEach开始====================");
//forEach 循环
//forEach () 方法不保证操作的顺序应用流中的每个元素。
//forEachOrdered () 方法按元素的顺序执行操作由流定义,可能会减慢并行流中的处理速度。
persons.stream().forEach(System.out::println);
System.out.println("==================forEach结束====================");
System.out.println("==================distinct开始====================");
//distinct:筛选,通过流所生成元素的 hashCode()和 equals 去除重复元素
Stream.of(1, 1, 3, 4, 5).distinct().forEach(System.out::println);
System.out.println("==================distinct结束====================");
System.out.println("==================limit开始====================");
//limit:截断流使其最多只包含指定数量的元素。
Stream.of(1, 1, 3, 4, 5).limit(3).forEach(System.out::println);
System.out.println("==================limit结束====================");
System.out.println("==================skip开始====================");
//skip:跳过元素,返回一个扔掉了前 n 个元素的流,若流中元素不足 n 个,则返回一个空流,与 limit(n)互补
Stream.of(1, 1, 3, 4, 5).skip(3).forEach(System.out::println);
System.out.println("==================skip结束====================");
System.out.println("==================sorted开始====================");
//sorted:对流进行排序。
Stream.of(2, 1, 5, 4, 3).sorted().forEach(System.out::println);
System.out.println("==================sorted结束====================");
System.out.println("==================dropWhile开始====================");
//dropWhile:从原始流起始位置开始删除满足指定 Predicate 的元素,直到遇到第一个不满足 Predicate 的元素。
Stream.of(2, 1, 5, 4, 3).dropWhile(n->n<3).forEach(System.out::println);
System.out.println("==================dropWhile结束====================");
System.out.println("==================takeWhile开始====================");
//takeWhile:从原始流起始位置开始保留满足指定 Predicate 的元素,直到遇到第一个不满足 Predicate 的元素。
Stream.of(2, 1, 5, 4, 3).takeWhile(n->n<3).forEach(System.out::println);
System.out.println("==================takeWhile结束====================");
System.out.println("==================filter开始====================");
//filter 过滤 返回与指定谓词匹配的流
//我们可以将两个滤波器组合为一个具有 AND 运算。
persons.stream().filter(p -> p.isMale() && p.getIncome() > 5000.0).map(Employee::getName).forEach(System.out::println);
System.out.println("==================filter结束====================");
System.out.println("==================map开始====================");
//流映射 映射操作对每个元素应用函数以产生另一流。输入和输出流中的元素数量是相同的。该操作不修改输入流的元素。
//<R> Stream<R> map(Function<? super T,? extends R> mapper)
//DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper)
//IntStream mapToInt(ToIntFunction<? super T> mapper)
//LongStream mapToLong(ToLongFunction<? super T> mapper)
//IntStream,LongStream 和 DoubleStream 也定义了映射函数。支持对 IntStream 进行映射操作的方法如下:
//IntStream map(IntUnaryOperator mapper)
//DoubleStream mapToDouble(IntToDoubleFunction mapper)
//LongStream mapToLong(IntToLongFunction mapper)
//<U> Stream<U> mapToObj(IntFunction<? extends U> mapper)
IntStream.rangeClosed(1, 5).map(n -> n * n).forEach(System.out::println);
System.out.println("==================map结束====================");
System.out.println("==================flatMap开始====================");
//flatMap 支持一对多映射。它将每个元素映射到流,然后将流的流平面化为流。
Stream.of(1, 2, 3).flatMap(n -> Stream.of(n, n+1)).forEach(System.out::println);
System.out.println("==================flatMap结束====================");
System.out.println("==================reduce开始====================");
//流组合
// reduce 操作采用两个称为种子(初始值)和累加器(函数)的参数。
// 种子和一个元素被传递给累加器,它返回部分结果。然后将部分结果和下一个元素传递给累加器函数。
//直到所有元素被传递到累加器。累加器返回的最后一个值是 reduce 操作的结果。
double sum = persons.stream().map(Employee::getIncome).reduce(0.0, Double::sum);
System.out.println(sum);
//如果流是空的,我们不能使用默认值为 0。
Optional<Integer> max = Stream.of(1, 2, 3, 4, 5).reduce(Integer::max);
if (max.isPresent()) {
System.out.println("max = " + max.get());
} else {
System.out.println("max is not defined.");
}
max = Stream.<Integer> empty().reduce(Integer::max);
if (max.isPresent()) {
System.out.println("max = " + max.get());
} else {
System.out.println("max is not defined.");
}
System.out.println("==================reduce结束====================");
System.out.println("==================流聚合开始====================");
//流聚合
//要计算数字流上的和,max,min,average 等,我们可以将非数字流映射到数值流类型 (IntStream,LongStream 或 DoubleStream),然后使用专门的方法
double totalIncome = persons
.stream()
.mapToDouble(Employee::getIncome)
.sum();
System.out.println("Total Income: " + totalIncome);
//要获取流的最小值和最大值,也可以使用特定流的 min () 和 max () 方法。
Optional<Employee> person = persons.stream()
.max(Comparator.comparingDouble(Employee::getIncome));
OptionalDouble income =
persons
.stream()
.mapToDouble(Employee::getIncome).max();
System.out.println("==================流聚合结束====================");
System.out.println("==================collect开始====================");
//流收集器 collect 方法重载了两个版本:
//<R> R collect(Supplier<R> supplier, BiConsumer<R,? super T> accumulator, BiConsumer<R,R> combiner)
//供应商提供一个可变容器来存储结果。
//累加器,将结果累积到可变容器中。
//组合器,当并行使用时组合部分结果。
List<String> names = persons
.stream()
.map(Employee::getName)
.collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
System.out.println(names);
//<R,A> R collect(Collector<? super T,A,R> collector)
//将 Collector 接口的一个实例作为参数,三种最常用的方法之一收集器类是 toList (),toSet () 和 toCollection ()。
List<String> names1 = persons
.stream()
.map(Employee::getName)
.collect(Collectors.toList());
System.out.println(names1);
System.out.println("==================collect结束====================");
System.out.println("==================Statistics开始====================");
// 流统计 java.util 包包含三个类来收集统计信息:
//DoubleSummaryStatistics
//LongSummaryStatistics
//IntSummaryStatistics
DoubleSummaryStatistics stats = new DoubleSummaryStatistics();
stats.accept(100.0);
stats.accept(300.0);
stats.accept(230.0);
stats.accept(532.0);
stats.accept(422.0);
long stats_count = stats.getCount();
double stats_sum = stats.getSum();
double stats_min = stats.getMin();
double stats_avg = stats.getAverage();
double stats_max = stats.getMax();
System.out.printf(
"count=%d, sum=%.2f, min=%.2f, average=%.2f, max=%.2f%n", stats_count, stats_sum,
stats_min, stats_max, stats_avg);
//流摘要统计
DoubleSummaryStatistics incomeStats = persons
.stream()
.map(Employee::getIncome)
.collect(DoubleSummaryStatistics::new,
DoubleSummaryStatistics::accept,
DoubleSummaryStatistics::combine);
System.out.println(incomeStats);
//数字流摘要统计;Collectors 类包含要计算的方法数字数据的特定类型的摘要统计信息。
//Collectors.summarizingDouble () 返回 DoubleSummaryStatistics。
//Collectors.summarizingLong () 返回一个 LongSummaryStatistics。
//Collectors.summarizingInt () 返回一个 IntSummaryStatistics。
DoubleSummaryStatistics incomeStats1 = persons
.stream()
.collect(Collectors.summarizingDouble(Employee::getIncome));
System.out.println(incomeStats1);
System.out.println("==================Statistics结束====================");
System.out.println("==================toMap开始====================");
//流收集映射
//toMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper)
//第一个参数将流元素映射到映射中的键。
//第二个参数将流元素映射到映射中的值。
Map<Long,String> idToNameMap = persons
.stream()
.collect(Collectors.toMap(Employee::getId, Employee::getName));
System.out.println(idToNameMap);
//toMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper, BinaryOperator<U> mergeFunction)
//第二个参数为合并函数,合并函数传递重复键的旧值和新值。 该函数应合并两个值,并返回将用于键的新值。
Map<Employee.Gender,String> genderToNamesMap =
persons
.stream()
.collect(Collectors.toMap(Employee::getGender,
Employee::getName,
(oldValue, newValue) -> String.join(", ", oldValue, newValue)));
System.out.println(genderToNamesMap);
System.out.println("==================toMap结束====================");
System.out.println("==================joining开始====================");
//流连接,Collectors 类的 joining () 方法返回一个收集器,它连接 CharSequence 流,并将结果作为 String 返回
//joining() 连接所有元素
String joiningnames = persons.stream()
.map(Employee::getName)
.collect(Collectors.joining());
System.out.println("Joined names: " + joiningnames);
//joining(CharSequence delimiter) 使用在两个元素之间使用的分隔符。
String delimitedNames = persons.stream()
.map(Employee::getName)
.collect(Collectors.joining(", "));
System.out.println("Joined, delimited names: " + delimitedNames);
//joining(CharSequence delimiter, CharSequence prefix, CharSequence suffix)使用分隔符,前缀和后缀
String prefixedNames = persons.stream()
.map(Employee::getName)
.collect(Collectors.joining(", ", "Hello ", ". Goodbye."));
System.out.println(prefixedNames);
System.out.println("==================joining结束====================");
System.out.println("==================groupingBy开始====================");
//流分组 Collectors 类中的 groupingBy () 方法返回一个收集器,用于在将数据收集到 Map 之前对数据进行分组。
//groupingBy(Function<? super T,? extends K> classifier)
//使用提供的字段对集合元素进行分组,返回一个 Map <字段,相同字段值的元素集>
Map<Employee.Gender, List<Employee>> countByGender = persons.stream()
.collect(Collectors.groupingBy(Employee::getGender));
System.out.println(countByGender);
//groupingBy(Function<? super T,? extends K> classifier, Collector<? super T,A,D> downstream)
//collector 对与每个键相关联的值执行简化操作。
Map<Employee.Gender, String> namesByGender = persons
.stream()
.collect(Collectors.groupingBy(Employee::getGender,
Collectors.mapping(Employee::getName, Collectors.joining(", "))));
System.out.println(namesByGender);
//嵌套实现二级分组
Map personsByGenderAndDobMonth = persons.stream()
.collect(
Collectors.groupingBy(
Employee::getGender,
Collectors.groupingBy(
p -> p.getDob().getMonth(),
Collectors.mapping(Employee::getName,Collectors.joining(", "))
)
)
);
System.out.println(personsByGenderAndDobMonth);
//groupingBy(Function<? super T,? extends K> classifier, Supplier<M> mapFactory, Collector<? super T,A,D> downstream)
//一个分组器,一个最终类型的生产者,一个收集器
TreeMap<String, Set<String>> map = persons.stream().collect(Collectors.groupingBy(Employee::getCity, TreeMap::new, Collectors.mapping(Employee::getName, Collectors.toSet())));
map.forEach((key,val)->{
System.out.println("城市:"+key+" 姓氏集:"+val);
});
System.out.println("==================groupingBy结束====================");
System.out.println("==================partitioningBy开始====================");
//流分区
// 分区是分组的一种特殊情况。
//分组数据基于从函数返回的键。可能有很多组。
//分区仅处理基于谓词的两个组。评估为 true 的值为一个组,false 为另一个组。
//partitioningBy(Predicate<? super T> predicate)
Map<Boolean, List<Employee>> partionedByMaleGender1 = persons.stream()
.collect(Collectors.partitioningBy(Employee::isMale));
System.out.println(partionedByMaleGender1);
//partitioningBy(Predicate<? super T> predicate, Collector<? super T,A,D> downstream)
//可以执行还原操作的收集器
Map<Boolean,String> partionedByMaleGender2 = persons.stream()
.collect(
Collectors.partitioningBy(
Employee::isMale,
Collectors.mapping(Employee::getName, Collectors.joining(", "))));
System.out.println(partionedByMaleGender2);
System.out.println("==================partitioningBy结束====================");
System.out.println("==================collectingAndThen开始====================");
//流转换收集器结果 我们可以将收集器的结果转换为不同的类型。
//collectingAndThen(Collector<T,A,R> downstream, Function<R,RR> finisher)
//第一个参数是收集数据的收集器。 第二个参数是转换结果的转换器。
List<String> collectingAndThen = persons.stream()
.map(Employee::getName)
.collect(Collectors.collectingAndThen(Collectors.toList(),
result -> Collections.unmodifiableList(result)));//不可更改视图。
System.out.println(collectingAndThen);
System.out.println("==================collectingAndThen结束====================");
System.out.println("==================findAny开始====================");
//流查找
// Find any male
Optional<Employee> anyMale = persons.stream().filter(Employee::isMale).findAny();//返回当前流中的任意元素
if (anyMale.isPresent()) {
System.out.println("Any male: " + anyMale.get().getName());
} else {
System.out.println("No male found.");
}
System.out.println("==================findAny结束====================");
System.out.println("==================findFirst开始====================");
// Find the first male
//诸如 IntStream,LongStream 和 DoubleStream 的原始类型流也包含与谓词一起使用的相同方法和用于原语类型的可选方法。
//所有查找操作都是终端操作。它们也是短路操作。短路操作可以不必处理整个流以返回结果。
Optional<Employee> firstMale = persons.stream().filter(Employee::isMale).findFirst();//返回第一个元素
if (firstMale.isPresent()) {
System.out.println("First male: " + anyMale.get());
} else {
System.out.println("No male found.");
}
System.out.println("==================findFirst结束====================");
System.out.println("==================allMales开始====================");
//流匹配
//boolean allMatch(Predicate<? super T> predicate) //匹配所以
boolean allMales = persons.stream().allMatch(Employee::isMale);
System.out.println("All males: " + allMales);
System.out.println("==================allMatch结束====================");
System.out.println("==================anyMatch开始====================");
//boolean anyMatch(Predicate<? super T> predicate) //匹配任意
boolean anyoneBornIn1971 = persons.stream().anyMatch(p -> p.getDob().getYear() == 1971);
System.out.println("Anyone born in 1971: " + anyoneBornIn1971);
System.out.println("==================anyMatch结束====================");
System.out.println("==================noneMatch开始====================");
//boolean noneMatch(Predicate<? super T> predicate) //不匹配
boolean noneMatch1971 = persons.stream().noneMatch(p -> p.getDob().getYear() == 1971);
System.out.println("noneMatch 1971: " + noneMatch1971);
System.out.println("==================noneMatch结束====================");
System.out.println("==================parallel开始====================");
//并行流
//Stream:串行流,单线程,线程安全,适合阻塞任务、重量级任务;
//parallelStream:并行流,多线程,线程不安全,其底层使用 Fork/Join 框架实现,适合较单纯的数据处理任务;
//流可以是顺序的或并行的。
//顺序流上的操作由一个线程串行处理。
//使用多个线程并行处理并行流上的操作
//对流使用 parallel () 方法将顺序流转换为并行流。
//对流使用 sequential () 方法将并行流转换为顺序流。
String parallel = persons.stream() // Produces a sequential stream
.filter(Employee::isMale) // Processed in serial
.parallel() // Produces a parallel stream
.map(Employee::getName) // Processed in parallel
.collect(Collectors.joining(", ")); // Processed in parallel
System.out.println(parallel);
System.out.println("==================parallel结束====================");
}
public static void main(String[] args) {
operateStream();
}
public static class Employee {
public static enum Gender {
MALE, FEMALE
}
private long id;
private String name;
private String city;
private Gender gender;
private LocalDate dob;
private double income;
public Employee(long id, String name,String city, Gender gender, LocalDate dob,
double income) {
this.id = id;
this.name = name;
this.city = city;
this.gender = gender;
this.dob = dob;
this.income = income;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public void setName(String name) {
this.name = name;
}
public Gender getGender() {
return gender;
}
public void setGender(Gender gender) {
this.gender = gender;
}
public LocalDate getDob() {
return dob;
}
public void setDob(LocalDate dob) {
this.dob = dob;
}
public double getIncome() {
return income;
}
public void setIncome(double income) {
this.income = income;
}
public boolean isMale() {
return this.gender == Gender.MALE;
}
public boolean isFemale() {
return this.gender == Gender.FEMALE;
}
public static List<Employee> persons() {
Employee p1 = new Employee(1, "Jake","昆明", Gender.MALE, LocalDate.of(1971,
Month.JANUARY, 1), 2343.0);
Employee p2 = new Employee(2, "Jack", "开远",Gender.MALE, LocalDate.of(1972,
Month.JULY, 21), 7100.0);
Employee p3 = new Employee(3, "Jane", "昆明",Gender.FEMALE, LocalDate.of(1973,
Month.MAY, 29), 5455.0);
Employee p4 = new Employee(4, "Jode", "大理",Gender.MALE, LocalDate.of(1974,
Month.OCTOBER, 16), 1800.0);
Employee p5 = new Employee(5, "Jeny", "昆明",Gender.FEMALE, LocalDate.of(1975,
Month.DECEMBER, 13), 1234.0);
Employee p6 = new Employee(6, "Jason", "开远",Gender.MALE, LocalDate.of(1976,
Month.JUNE, 9), 3211.0);
List<Employee> persons = Arrays.asList(p1, p2, p3, p4, p5, p6);
return persons;
}
}
}
Java 流操作
于 2023-08-24 16:44:41 首次发布