@Data
@AllArgsConstructor
public class Student {
private String name;
private Integer score;
private Integer age;
}
public class StreamTest {
/**
* flatMap 接收一个流的来源(比如list), 返回一个流
* 将多个流,打平, 使其成为一个流
*/
@Test
public void Test3() {
Stream<List<Integer>> stream = Stream.of(Arrays.asList(1), Arrays.asList(2,3), Arrays.asList(4,5,6));
stream.flatMap(list -> list.stream()).map(x->x*x).forEach(System.out::println);
List<String> list2 = Arrays.asList("hello world", "world hello", "hello world hello", "hello welcome");
list2.stream().map(item -> item.split(" ")) // 拆成单词
.flatMap(Arrays::stream) // 打平, 将每个数组处理成流
.distinct() // 去重, 根据equals方法
.forEach(System.out::println);
}
/**
* flatMap的更深入使用, 核心就在于将多个stream打平成一个stream, 其入参就是stream类型的
* 利用两个list交叉打招呼, 相当于两个list的求笛卡尔积
*/
@Test
public void Test31() {
List<String> list1 = Arrays.asList("Hi", "Hello", "你好");
List<String> list2 = Arrays.asList("zhangsan", "lisi", "wangwu", "zhaoliu");
List<String> result = list1.stream()
.flatMap(item -> list2.stream().map(item2 -> item + " " + item2))
.collect(Collectors.toList());
result.forEach(System.out::println);
}
@Test
public void Test4() {
// generate方法, 传入一个生产者
Stream<String> stream = Stream.generate(UUID.randomUUID()::toString);
// findFirst 返回一个 optional
stream.findFirst().ifPresent(System.out::println);
}
/**
* Stream.iterate()
* 接收一个种子, 和一个行为
* 这个行为会以种子为初始值, 不断的累加执行,返回一个无限的串行流
* 所以在使用这个方法时, 一般会加一个limit()方法, 来限制长度
*/
@Test
public void Test5() {
Stream.iterate(1, item -> item + 2)
.limit(6)
.forEach(System.out::println);
System.out.println("-------------------------");
IntSummaryStatistics collect = Stream.iterate(1, item -> item + 2)
.limit(6)
.filter(x -> x > 2) // 过滤
.map(x -> x * 2) // 转换
.skip(2) // 忽略前两个元素
.limit(2) // 只取前两个元素
.collect(Collectors.summarizingInt(x -> x));// 求出结果集,集中包含很多操作
System.out.println(collect.getSum());
}
/**
* 串行流与并行流的排序时间对比
* 串行流 : 9619
*/
@Test
public void Test6() {
List<String> list = new ArrayList<>(5000000);
for (int i = 0; i < 5000000; ++i) {
list.add(UUID.randomUUID().toString());
}
System.out.println("开始排序");
long startTime = System.nanoTime();
list.stream().sorted().findFirst();
long endTime = System.nanoTime();
long millis = TimeUnit.NANOSECONDS.toMillis(endTime - startTime);
System.out.println("排序耗时: " +millis);
}
/**
* 串行流与并行流的排序时间对比
* 并行流: 2235
*/
@Test
public void Test7() {
List<String> list = new ArrayList<>(5000000);
for (int i = 0; i < 5000000; ++i) {
list.add(UUID.randomUUID().toString());
}
System.out.println("开始排序");
long startTime = System.nanoTime();
list.parallelStream().sorted().findFirst();
long endTime = System.nanoTime();
long millis = TimeUnit.NANOSECONDS.toMillis(endTime - startTime);
System.out.println("排序耗时: " +millis);
}
/**
* stream还提供了和sql一样的分组功能, 分组结果一般是返回map
* 根据谁分组, 谁就是key
*/
@Test
public void Test8() {
Student s1 = new Student("zhangsan", 100, 20);
Student s2 = new Student("lisi", 90, 20);
Student s3 = new Student("wangwu", 90, 30);
Student s4 = new Student("zhangsan", 80, 40);
List<Student> students = Arrays.asList(s1, s2, s3, s4);
// 按名字对学生分组
/* 传统做法:
结果: Map<String, List<Student>>
* 1. 循环列表
* 2. 取出学生名字
* 3. 检查map中是否存在该名字, 不存在直接添加到该map,
* 存在则将map中的List对象取出来,然后添加到list中
* 4. 返回map对象
* */
// stream做法:
Map<String, List<Student>> map = students.stream().collect(Collectors.groupingBy(Student::getName));
map.forEach((key, value) -> System.out.println("key: " + key + "
value: " + value));
System.out.println("-----------------------------------------");
// 根据分数来分组
Map<Integer, List<Student>> map2 = students.stream().collect(Collectors.groupingBy(Student::getScore));
map2.forEach((key, value) -> System.out.println("key: " + key + "
value: " + value));
System.out.println("-----------------------------------------");
// 分组后,返回数量
Map<String, Long> m3 = students.stream().collect(Collectors.groupingBy(Student::getName, Collectors.counting()));
m3.forEach((key, value) -> System.out.println("key: " + key + "
value: " + value));
System.out.println("-----------------------------------------");
// 分组后,返回分数平均值
Map<String, Double> m4 = students.stream().collect(Collectors.groupingBy(Student::getName, Collectors.averagingDouble(Student::getScore)));
m4.forEach((key, value) -> System.out.println("key: " + key + "
value: " + value));
}
/**
* stream分区, 分区是分组的一种特例, 只把数据分为两组
* 返回一个map, key是bool类型, 表明是否符合分区条件
*/
@Test
public void Test9() {
Student s1 = new Student("zhangsan", 100, 20);
Student s2 = new Student("lisi", 90, 20);
Student s3 = new Student("wangwu", 90, 30);
Student s4 = new Student("zhangsan", 80, 40);
List<Student> students = Arrays.asList(s1, s2, s3, s4);
Map<Boolean, List<Student>> map = students.stream()
.collect(Collectors.partitioningBy(student -> student.getScore() >= 90));
map.forEach((key, value) -> System.out.println("key: " + key + "
value: " + value));
}
@Test
public void Test() {
Student s1 = new Student("zhangsan", 80);
Student s2 = new Student("lisi", 90);
Student s3 = new Student("wangwu", 100);
Student s4 = new Student("zhaoliu", 90);
Student s5 = new Student("zhangsan", 70);
Student s6 = new Student("wangwu", 50);
List<Student> students = Arrays.asList(s1, s2, s3, s4, s5, s6);
// 在分组的基础上,再次分组, 先根据分数分组, 再根据名字分组
Map<Integer, Map<String, List<Student>>> map = students.stream().collect(Collectors.groupingBy(Student::getScore, Collectors.groupingBy(Student::getName)));
System.out.println(map);
System.out.println("-------------------------");
// 两次分区
Map<Boolean, Map<Boolean, List<Student>>> map2 = students.stream().collect(Collectors.partitioningBy(s -> s.getScore() > 80, Collectors.partitioningBy(s -> s.getScore() > 90)));
System.out.println(map2);
System.out.println("-------------------------");
// 先分区, 再求出分区中学生数量
Map<Boolean, Long> map3 = students.stream().collect(Collectors.partitioningBy(s -> s.getScore() > 80, Collectors.counting()));
System.out.println(map3);
System.out.println("-----------------------------");
// 先根据名字进行分组, 再收集组中分数最小的学生
Map<String, Optional<Student>> map4 = students.stream().collect(Collectors.groupingBy(Student::getName, Collectors.minBy(Comparator.comparingInt(Student::getScore))));
System.out.println(map4);
System.out.println("---------------------------------");
// 对map4 的优化,先根据名字进行分组, 再收集组中分数最小的学生, 由于先分组了,
// 所以后面比较的时候一定有元素,可以直接get到值,少一次Optional操作
Map<String, Student> map5 = students.stream()
.collect(Collectors.groupingBy(Student::getName,
Collectors.collectingAndThen(
Collectors.minBy(Comparator.comparingInt(Student::getScore)),
Optional::get)
)
);
System.out.println(map5);
System.out.println("---------------------------------");
}
}