概述
了解stream
Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一个则是 Stream API(java.util.stream.*)。
Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。 使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之, Stream API 提供了一种高效且易于使用的处理数据的方式。
流(Stream): 是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
这里的Stream和IO中的Stream不同,它提供了对集合操作的增强,极大的提高了操作集合对象的便利性。
“集合讲的是数据,流讲的是计算!”
特点
Stream自己不会存储元素;Stream不会改变源对象; 相反,它们会返回一个持有结果的新Stream;Stream操作是延迟执行的; 这意味着它们会等到需要结果的时候,才执行;
Stream 操作的三个步骤
- 创建 Stream
一个数据源(如: 集合,数组),获取一个流;
如:可以通过 Collection 集合提供的串行流 stream()或并行流 parallelStream() - 中间操作
一个中间操作链,对数据源的数据进行处理; - 终止操作(终端操作)
一个终止操作,执行中间操作链,并产生结果;

1.创建stream
1.Collection 扩展接口,提供了两个获取流的方法
default Stream<E> stream(): 返回一个顺序流default Stream<E> parallelStream(): 返回一个并行流
2.由数组创建流
Java8 中的 Arrays 的静态方法 stream() 可以获取数组流:
static <T> Stream<T> stream(T[] array): 返回一个流
重载形式,能够处理对应基本类型的数组:
public static IntStream stream(int[] array)public static LongStream stream(long[] array)public static DoubleStream stream(double[] array)
3.由Stream静态方法创建
- 由值创建流
Stream.of()
可以使用静态方法Stream.of(), 通过显示值 创建一个流。它可以接收任意数量的参数。
–public static<T> Stream<T> of(T... values): 返回一个流 - 由函数创建流:无限流
Stream.iterate()或Stream.generate()
– 迭代:public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
– 生成:public static<T> Stream<T> generate(Supplier<T> s)
@Test
public void test1(){
//1通过Collection系列集合提供的stream()或 paralleStream()
List<String> list = new ArrayList<>();
Stream stream1 = list.stream();
Stream stream2 = list.parallelStream();
//2.通过Arrays中的静态方法stream()
Employee[] employees = new Employee[8];
Stream<Employee> stream3 = Arrays.stream(employees);
//3.通过Stream类的静态放of()、iterate()、generate()创建
//of
Stream<String> stream4 = Stream.of("a","b","c");
//无限流1:iterate
Stream<Integer> stream5=Stream.iterate(0,x->x+2);
stream5.limit(5).forEach(x->System.out.print(x)); //02468
//无限流2:generate
Stream<Integer> stream6 = Stream.generate(()->(int)(Math.random()*100));
stream6.limit(6).forEach(x-> System.out.print(x));
}
2.中间操作
筛选与切片
filter接收lambda,从流中排除某些元素limit截断流,使其元素不穿过给定数量。limit在filter后一起使用可以短路skip(n)跳过元素,返回一个丢掉前n个元素的流,若流不足n个,返回一个空流,与limit互补distinct去重,通过元素的hashCode() 和 equals()比较去重
映射
map接收lambda 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成为一个新的元素flatMap接收一个函数为参数,将流中的每个值换成另一个流,然后把所有的流连接成一个流
排序
sorted()自然排序sorted(Compator com)定制排序
filter
/**
* filter
* 内部迭代:迭代操作由StreamAPI完成
*/
@Test
public void test1(){
//中间操作 不会执行任何操作
List<Employee> employees = Lists.newArrayList(new Employee(12, 1000), new Employee(20, 2000));
Stream<Employee> stream = employees.stream()
.filter((e) -> {
System.out.println("中间操作");
return e.getAge() > 13;
});
//终止操作 一次性执行全部内容,即“惰性求值”
stream.forEach(System.out::println); //Employee(age=20, salary=2000)
}
limit
/**
* 截断流 一旦找到满足条件的数据,后续的迭代结束
*/
@Test
public void test3(){
List<Employee> employees = Lists.newArrayList(new Employee(12, 1000), new Employee(20, 2000));
employees.stream()
.filter((e)->{
System.out.println("短路!");
return e.getSalary()>60;
})
.limit(1)
.forEach(System.out::println);
}
//短路!
//Employee(age=12, salary=1000)
skip
/**
* 跳过
*/
@Test
public void test4(){
List<Employee> employees = Lists.newArrayList(new Employee(12, 1000), new Employee(20, 2000));
employees.stream()
.filter(e->e.getSalary()>60)
.skip(1)
.forEach(System.out::println);
//Employee(age=20, salary=2000)
}
distinct
/**
* 去重 重写hashCode和equals
*/
@Test
public void test5(){
List<String> ss = Lists.newArrayList("a","a","b","c","c");
ss.stream().distinct()
.forEach(System.out::print);//abc
}
map和flapMap
二者区别 https://blog.youkuaiyun.com/andyzhaojianhui/article/details/79047825
类似add于addAll的关系。add参数是集合每个元素,返回至集合。addAll参数是整个集合,实际操作是加入的集合中每个元素,返回至集合。
map
map的返回结果:取决于map中的函数返回类型,返回结果是什么就是什么,再加上外层的Stream
//map
@Test
public void test6(){
/**
* map
*/
List<String> list = Arrays.asList("a","b","c");
System.out.println(list); //[a, b, c]
//将list中的每个元素转换成数字
Stream<Integer> stream = list.stream().map(s->{
Integer i;
switch (s){
case "a":i=1;break;
case "b":i=2;break;
case "c":i=3;break;
default:i=0;
}
return i;
});
stream.forEach(System.out::print); //123
}
flatMap在取深层元素时很有用。
flatMap的返回结果中,两层Sream包裹的仅保留一层Stream,并去掉集合的一层。
作用1:【输入】将输入去掉一层集合后返回 借助u->u.stream()
//map与flatMap对比
@Test
public void test7(){
/**
* flatMap
*/
List<String> l1= Arrays.asList("a","b","c");
System.out.println(l1);//[a, b]
List<String> l2= Arrays.asList("x","y");
System.out.println(l2);//[x, y]
//map
Stream<List<String>> s =Stream.of(l1,l2);
//map的返回结果:取决于map中的函数返回类型,返回结果是什么就是什么,再加上外层的Stream
Stream<List> s1 = s.map(li -> Lists.newArrayList(li,"1"));
s1.forEach(System.out::println); //[[a, b, c], 1][[x, y], 1]
//flatMap
Stream<List<String>> ss =Stream.of(l1,l2);
//flatMap作用1:将输入去掉一层后返回
Stream<String> s2 = ss.flatMap(u->u.stream());
s2.forEach(System.out::print);
//测试多层Stream和List的情况:仅保留一层Stream并去掉集合的一层
List<List<String>> lists = Lists.newArrayList();
List<String> ls1=Lists.newArrayList("didi");
List<String> ls2=Lists.newArrayList("hi","my");
lists.add(ls1);
lists.add(ls2);
System.out.println(lists); //[[didi], [hi, my]]
Stream<List<List<String>>> si = Stream.of(lists);
Stream<List<String>> s3 = si.flatMap(u->u.stream());
s3.forEach(System.out::print); //[didi][hi, my] 由此可见仅去掉了一层
}
作用2:【输出】flatMap中的函数返回,如果也是Stream,则仅保留一层stream
// flatMap的返回结果测试
public Stream<Character> characterStream(String s) {
List<Character> result = new ArrayList<>();
for (char c : s.toCharArray())
result.add(c);
return result.stream(); //list转stream,一个级别上
}
@Test
public void test8(){
List<String> words = new ArrayList<String>();
words.add("your");
words.add("name");
Stream<Stream<Character>> result1 = words.stream().map(this::characterStream);
//测试map
result1.forEach(System.out::println);//打印的是两个Stream对象
//java.util.stream.ReferencePipeline$Head@12bb4df8
//java.util.stream.ReferencePipeline$Head@4cc77c2e
Stream<Character> result2 = words.stream().flatMap(this::characterStream);
//测试flatMap
result2.forEach(System.out::println); //一个个的字符
}
flatmap示例2:
@Test
public void testMapAndFlatMap() {
List<String> words = new ArrayList<String>();
words.add("hello");
words.add("word");
//将words数组中的元素再按照字符拆分,然后字符去重,最终达到["h", "e", "l", "o", "w", "r", "d"]
//如果使用map,是达不到直接转化成List<String>的结果
List<String> stringList = words.stream()
.flatMap(word -> Arrays.stream(word.split("")))
.distinct()
.collect(Collectors.toList());
stringList.forEach(e -> System.out.println(e));
}
sorted() 自然排序(Comparable)
sorted(Compator com) 定制排序 (Comparator)
List<String> list = Arrays.asList("bbb","zzz","yyy","ccc","sss");
list.stream()
.sorted()
.forEach(System.out::println);
// 员工排序
employees.stream()
.sorted((e1,e2) -> {
if(e1.getAge().equals(e2.getAge())){
return e1.getName().compareTo(e2.getNme());
}else{
return e1.getAge().compareTo(e2.getAge());
}
}).forEach(System.out::println);
3.终止操作
终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是 void 。
查找与匹配
- 查找与匹配
allMatch检查是否匹配所有元素anyMacth检查是否至少匹配一个元素noneMacth检查是否没有匹配所有元素findFirst返回第一个元素findAny返回任意一个元素count返回元素总数max返回最大值min返回最小值forEach内部迭代(使用 Collection 接口需要用户去做迭 代,称为外部迭代。相反,Stream API 使用内部 迭代——它帮你把迭代做了)
归约
reduce(T identity,BinaryOperator) 可以将流中元素反复结合起来,得到一个值。 返回 Treduce(BinaryOperator) 可以将流中元素反复结合起来,得到一个值。 返回 Optional
收集
collect(Collector c)将流转换为其他形式。接收一个 Collector接口的 实现,用于给Stream中元素做汇总的方法
备注:map 和 reduce 的连接通常称为 map-reduce 模式,因 Google 用它 来进行网络搜索而出名
查找匹配demo
@Test
public void test1(){
boolean b1 = studentList.stream()
.allMatch((e) -> e.getStatus() == Student.Status.FREE);
System.out.println(b1);
boolean b2 = studentList.stream()
.anyMatch((e) -> e.getStatus() == Student.Status.FREE);
System.out.println(b2);
boolean b3 = studentList.stream()
.noneMatch((e) -> e.getStatus() == Student.Status.FREE);
System.out.println(b3);
Optional<Student> op1 = studentList.stream()
.sorted((e1, e2) -> -Integer.compare(e1.getScore(), e2.getScore()))
.findFirst();
System.out.println(op1.get());
Optional<Student> op2 = studentList.stream()
.filter((e) -> e.getStatus() == Student.Status.FREE)
.findAny();
System.out.println(op2.get());
//并行流
Optional<Student> op3 = studentList.parallelStream()
.filter((e) -> e.getStatus() == Student.Status.FREE)
.findAny();
System.out.println(op3.get());
//count
long count = studentList.stream()
.count();
System.out.println(count);
//max
Optional<Student> max = studentList.stream()
.max(Comparator.comparing(Student::getName));
System.out.println(max.get());
//min
Optional<Integer> min = studentList.stream()
.map(e -> e.getScore())
.min(Integer::compare);
System.out.println(min.get());
}
reduce
//reduce
@Test
public void test9(){
/*
* 归约
* reduce(T identity, BinaryOperator) / reduce(BinaryOperator)
* 可以将流中元素反复结合起来,得到一个值; 其中 第一个参数identity,表示起始值
*/
// 需求: 将list集合中的元素相加
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Integer sum = list.stream()
.reduce(0,(x,y) -> x+y);
System.out.println(sum); //55
List<Employee> employees = Lists.newArrayList(new Employee(12, 1000), new Employee(20, 2000));
// 获取员工工资的总和
Optional<Integer> op = employees.stream()
.map(Employee::getSalary)
.reduce(Integer::sum);
System.out.println(op.get()); //3000
}
collect
Collector 接口中方法的实现决定了如何对流执行收集操作(如收 集到 List、Set、Map)。但是 Collectors 实用类提供了很多静态 方法,可以方便地创建常见收集器实例,具体方法与实例如下表:


//collect
@Test
public void test10(){
/*
* 收集
* collect: 将流转换为其他形式,接收一个Collector 接口的实现,用于给 Stream 中元素做汇总的方法
* Collector 接口中方法的实现决定了如何对流执行收集操作(如收集到 List, Set, Map)
* Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例;
*/
List<Employee> employees = Lists.newArrayList(new Employee(12, 1000), new Employee(20, 2000));
// 需求: 将员工中年龄收集到 list 集合
List<Integer> list = employees.stream()
.map(Employee::getAge)
.collect(Collectors.toList());
list.forEach(System.out::println);
// 将员工姓名收集到 HashSet 中
HashSet<Integer> hset = employees.stream()
.map(Employee::getAge)
.collect(Collectors.toCollection(HashSet::new)); //toSet()
// 员工总人数
Long count = employees.stream()
.collect(Collectors.counting());
System.out.println("总数"+count);
// 工资平均值
Double avg = employees.stream()
.collect(Collectors.averagingDouble(Employee::getSalary));
System.out.println("平均"+avg);
// 工资总和
Double sum = employees.stream()
.collect(Collectors.summingDouble(Employee::getSalary));
System.out.println("综合"+sum);
// 按照Status进行分组
// Map<Status, List<Employee>> map = employees.stream()
// .collect(Collectors.groupingBy(Employee::getStatus));
// System.out.println(map);
// 分区(满足条件的一个区,不满足条件的在另一个区)
Map<Boolean, List<Employee>> map = employees.stream()
.collect(Collectors.partitioningBy((e) -> e.getSalary() > 1700));
System.out.println(map); //{false=[Employee(age=12, salary=1000)], true=[Employee(age=20, salary=2000)]}
DoubleSummaryStatistics dss = employees.stream()
.collect(Collectors.summarizingDouble(Employee::getSalary));
System.out.println(dss.getSum());
System.out.println(dss.getAverage());
System.out.println(dss.getMax());
// 连接字符串
// String str = employees.stream()
// .map(Employee::getName)
// .collect(Collectors.joining(","));
// System.out.println(str);
}
其他demo。参考 https://blog.youkuaiyun.com/keepstrong/article/details/80154295
分组
/**
* 分组
*/
@Test
public void test5(){
Map<Student.Status, List<Student>> map = studentList.stream()
.collect(Collectors.groupingBy(Student::getStatus));
System.out.println(map);
}
@Test
public void test6(){
Map<Student.Status, Map<String, List<Student>>> map = studentList.stream()
.collect(Collectors.groupingBy(Student::getStatus, Collectors.groupingBy((e) -> {
if (e.getAge() < 15) {
return "小朋友";
} else {
return "青年";
}
})));
System.out.println(map);
}
分区
@Test
public void test7(){
Map<Boolean, List<Student>> map = studentList.stream()
.collect(Collectors.partitioningBy((e) -> e.getScore() > 70));
System.out.println(map);
}
统计
@Test
public void test8(){
IntSummaryStatistics iss = studentList.stream()
.collect(Collectors.summarizingInt(Student::getScore));
System.out.println(iss.getSum());
System.out.println(iss.getAverage());
System.out.println(iss.getCount());
System.out.println(iss.getMax());
}
拼接
@Test
public void test9(){
String str = studentList.stream()
.map(Student::getName)
.collect(Collectors.joining(","));
System.out.println(str);
}
本文深入解析Java 8中的Stream API,介绍其核心概念与特性,包括Stream的特点、操作流程及创建、中间操作、终止操作等关键环节。通过实战案例,展示如何利用Stream API进行数据处理,提高代码效率。
9364

被折叠的 条评论
为什么被折叠?



