文章13 | 阅读 6315 | 点赞0
java8已经诞生很久了,stream-api 你是否已经非常的精通了?
了解 Stream-api 的前提需要熟悉常见函数式接口,对于每一个方法你是否有深入的解读过?
本篇文章主要目的是解读这个接口的每一个方法以及对应的应用场景。
跳过流的创建,重点讲解每一个方法的功能和应用场景。
stream用的最多的场景就是从数据库查询得到List数据,可能这些数据需要经过一些处理才可以暴露给前端。
其目的也是为了避免写复杂的SQL语句,很早以前的在没有持久层框架的开发,sql的基本功就要求非常高,后随着sql的持久层框架出现,很多场景更愿意用java代码来实现数据的转换,而Stream的产生也是为了更好的利用java来实现数据的转换。
持久层框架的出现,弱化了sql的功能,数据库层更偏向于数据存储,数据处理更适合用java等其它方式进行处理。
保留返回条件为true的元素
/** * @author 诗水人间 * @link 博客:{https://yumbo.blog.csdn.net/} * @link github:{https://github.com/1015770492} * @date 2021/9/19 21:32 */
public class FilterDemo {
public static void main(String[] args) {
// 保留符合条件的值,打印2,4
Arrays.asList(1, 2, 3, 4, 5).stream().filter(x -> x % 2 == 0).forEach(System.out::println);
}
}
遍历元素,每一个遍历都需要return一个元素(可以是原来的也可以是新的)
这个map方法最主要的功能就是function类型函数式接口,就是将 A 转换成 B
/** * @author 诗水人间 * @link 博客:{https://yumbo.blog.csdn.net/} * @link github:{https://github.com/1015770492} * @date 2021/9/19 21:41 */
public class MapDemo {
public static void main(String[] args) {
// map映射,打印A1,A2,A3,A4,A5
Arrays.asList(1,2,3,4,5).stream().map(x->"A"+x).forEach(System.out::println);
}
}
功能相似所以放一起,返回对应的int、long、double类型的stream
该功能和map类似,只不过是结果一定是对应类型的stream
/** * @author 诗水人间 * @link 博客:{https://yumbo.blog.csdn.net/} * @link github:{https://github.com/1015770492} * @date 2021/9/19 21:47 */
public class MapToNumDemo {
public static void main(String[] args) {
// 将字符串类型的数转换为int
Arrays.asList("1","2","3").stream().mapToInt(Integer::parseInt).forEach(System.out::println);
// 将字符串类型的数转换为long
Arrays.asList("1","2","3").stream().mapToLong(Long::parseLong).forEach(System.out::println);
// 将字符串类型的数转换为double
Arrays.asList("1","2","3").stream().mapToDouble(Double::parseDouble).forEach(System.out::println);
}
}
处理二维或多维数组,将元素降维
public class FlatMapDemo {
public static void main(String[] args) {
// 相当于二维数组
final List<List<String>> lists = Arrays.asList(
Arrays.asList("Hello World !!!", "I am Tom", "How are you doing?"),
Arrays.asList("1", "2", "3"),
Arrays.asList("你好世界!!!", "我是tom", "你正在做什么?")
);
String result="";
// flatMap需要返回的上一个Stream类型的
lists.stream().flatMap(x->x.stream()).forEach(System.out::println);
lists.stream().map(x->x.stream()).forEach(System.out::println);
}
}
前面讲过了 faltMap
传入的函数式接口返回值类型一定是一个Stream流,故ToInt、ToLong、ToDouble就是返回IntStream、LongStream、DoubleStream
底层是Set去重,意味着要使用distinct方法对象必须重写equals方法和hashCode方法(hashCode主要是为了避免没有必要的hash冲突)
/** * @author 诗水人间 * @link 博客:{https://yumbo.blog.csdn.net/} * @link github:{https://github.com/1015770492} * @date 2021/9/19 22:12 */
public class DistinctDemo {
public static void main(String[] args) {
// 打印 100,101,103,去除的底层原来就是调用对象的equals方法,
// 实际上底层就是用一个Set保存所有元素,利用set自动去重,set的底层又是Hashmap,利用key的不重复
Arrays.asList("100","101","100","103").stream().distinct().forEach(System.out::println);
}
}
Comparator
接口可以类比sql的 orderBy 需要注意的是对象一定要是可以比较的,也既是否实现了Comparator
接口
/** * @author 诗水人间 * @link 博客:{https://yumbo.blog.csdn.net/} * @link github:{https://github.com/1015770492} * @date 2021/9/19 22:22 */
public class SortedDemo {
public static void main(String[] args) {
// 打印 13,15,20,23,50
Arrays.asList(20, 15, 13, 23, 50).stream().sorted().forEach(System.out::println);
// 如果自定义对象可以传入一个比较器
Arrays.asList(21, 16, 14, 24, 51).stream().sorted(Integer::compareTo).forEach(System.out::println);
// 不可以比较的可以传入一个Comparator接口
Arrays.asList("22", "17", "15", "25", "52").stream().sorted((x, y) -> {
return Integer.parseInt(x) - Integer.parseInt(y);
}).forEach(System.out::println);
// 不使用lambda表达式可以这样写,其它对象也可以参考下面的写法
Arrays.asList("22", "17", "15", "25", "52").stream().sorted(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return Integer.parseInt(o1) - Integer.parseInt(o2);
}
}).forEach(System.out::println);
}
}
会在元素被Consumer消费前触发,不会修改元素本身,对象元素内部的属性可以被改变,但是对象本身不会被替换
import lombok.Builder;
import lombok.Data;
import java.util.Arrays;
/** * @author 诗水人间 * @link 博客:{https://yumbo.blog.csdn.net/} * @link github:{https://github.com/1015770492} * @date 2021/9/19 22:30 */
@Data
@Builder
class User {
int age;
}
public class PeekDemo {
public static void main(String[] args) {
Arrays.asList(
User.builder().age(1).build(),
User.builder().age(2).build(),
User.builder().age(3).build(),
User.builder().age(4).build(),
User.builder().age(5).build()
).stream().peek(x -> {
System.out.println(x.age);
x.setAge(x.age + 100);
x = User.builder().age(3).build();
}).forEach(System.out::println);
}
}
peek不会修改stream的元素,诞生可以修改元素内部的属性(例如讲所有年龄加了100),打印结果
1
User(age=101)
2
User(age=102)
3
User(age=103)
4
User(age=104)
5
User(age=105)
import java.util.stream.IntStream;
/** * @author 诗水人间 * @link 博客:{https://yumbo.blog.csdn.net/} * @link github:{https://github.com/1015770492} * @date 2021/9/19 22:54 */
public class LimitDemo {
public static void main(String[] args) {
// 打印1-10
IntStream.rangeClosed(1,10).forEach(System.out::println);
// 限制流元素个数为3个,打印1,2,3
IntStream.rangeClosed(1,10).limit(3).forEach(System.out::println);
}
}
import java.util.stream.IntStream;
/** * @author 诗水人间 * @link 博客:{https://yumbo.blog.csdn.net/} * @link github:{https://github.com/1015770492} * @date 2021/9/19 22:58 */
public class SkipDemo {
public static void main(String[] args) {
// 1-10,跳过前3个元素,限制元素为2个,故打印4,5
IntStream.rangeClosed(1,10).skip(3).limit(2).forEach(System.out::println);
}
}
这个方法在每一个示例代码中都用到了,也比较简单省略。
Stream流元素转换为数组
方法比较简单省略说明
需要注意的是,它有2个方法,可以传一个Function类型的函数,意味着可以进行一些类型转换或者A->B的操作
reduce有3个方法
,传一个参数,两个参数,三个参数
其中最常用的是传一个参数的
import java.util.Arrays;
/** * @author 诗水人间 * @link 博客:{https://yumbo.blog.csdn.net/} * @link github:{https://github.com/1015770492} * @date 2021/9/19 23:05 */
public class ReduceDemo {
public static void main(String[] args) {
System.out.println(Arrays.asList("1", "2", "3", "4").stream().reduce((x, y) -> {
System.out.println(x + "---" + y);
return "y"+y;
}).get());
}
}
会发现这里传入的接口需要接收两个参数,默认第一个参数x使用第一个元素,也就是"1"
,而另外两个reduce的区别就是这个这个默认值不一定是第一个元素,可以通过手动传入。
后将本次的操作的返回值作为下一个操作的x传入,然后接着从流中取出一个元素。
因此如下打印
第一次的返回值是 "y2"
,传入x,y=3,也就打印了y2---3
1---2
y2---3
y3---4
y4
import java.util.Arrays;
/** * @author 诗水人间 * @link 博客:{https://yumbo.blog.csdn.net/} * @link github:{https://github.com/1015770492} * @date 2021/9/19 23:05 */
public class ReduceDemo {
public static void main(String[] args) {
System.out.println(Arrays.asList("1", "2", "3", "4").stream().reduce("init", (x, y) -> {
System.out.println(x + "---" + y);
return "y" + y;
}));
}
}
如下打印结果
init---1
y1---2
y2---3
y3---4
y4
parallel
下才会触发第三个参数第3个参数似乎是在并发的场景下使用的。其目的就是利用fork/join框架的思想,第二个参数只是一个拆分任务的方法,真正的合并结果的是第3个参数的lambda表达式,中间的那个lambda表达式作用是拆分大任务的过程
这个方法很有用哦,可以解决并发场景下的计算,而不用写一堆的ForkJoin接口对应的实现。
import java.util.Arrays;
/** * @author 诗水人间 * @link 博客:{https://yumbo.blog.csdn.net/} * @link github:{https://github.com/1015770492} * @date 2021/9/19 23:05 */
public class ReduceDemo {
public static void main(String[] args) throws Exception {
// 一定要是parallel并行流才会触发后面的
final Integer reduce = Arrays.asList("1", "2", "3", "4").stream().parallel().reduce(0, (u, v) -> {
System.out.println("第一个接口 ");
System.out.println(u + "u---v" + v);
return Integer.parseInt(v);// 如果改成return 1;那么打印的结果会是4,而不是10,相当于这里是做拆分任务用的
}, (x, y) -> {
System.out.println("第二个接口 ");
System.out.println(x + "x---y" + y);
return x+y;
});
System.out.println(reduce);
}
}
此方法需要传入一个收集器,也就是Collector接口的实现,因此常用会用到工具类Collectors 其内部实现了该接口的一些方法,有常用的List、Map、Collection、Set。同时还可以实现数据的分组哦(按照类成员属性进行分组)
这个方法经常会使用到,因为常常我们会从数据库查询,经过一些操作后得到一些新的元素,然后我们会将这些新的元素收集起来做一些其它操作
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/** * @author 诗水人间 * @link 博客:{https://yumbo.blog.csdn.net/} * @link github:{https://github.com/1015770492} * @date 2021/9/20 0:06 */
public class CollectDemo {
public static void main(String[] args) {
// 转换为字符串数组
final List<String> collect = Arrays.asList(1, 2, 3, 4).stream()
.map(String::valueOf).collect(Collectors.toList());
}
}
import lombok.Builder;
import lombok.Data;
import java.util.*;
import java.util.stream.Collectors;
/** * @author jinhua * @date 2021/8/19 21:30 */
@Data
@Builder
class User{
String name;
String sex;
int age;
}
public class Demo {
public static void main(String[] args) throws Exception {
final ArrayList<User> users = new ArrayList<>();
for (int i = 0; i < 3; i++) {
users.add(User.builder().name(String.valueOf(i)).sex("男").age(11).build());
}
Map<String, Object> map = users.stream()
.collect(Collectors.toMap(k -> k.name + k.sex, v -> v.age));
map.forEach((k,v)->{
System.out.println(k+" "+v);
});
}
}
按照年龄分组
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
class User {
int age;
}
/** * @author 诗水人间 * @link 博客:{https://yumbo.blog.csdn.net/} * @link github:{https://github.com/1015770492} * @date 2021/9/20 0:06 */
public class CollectDemo {
public static void main(String[] args) {
// 按照年龄进行分组,得到一个Map
final Map<Integer, List<User>> collect = Arrays.asList(
User.builder().age(1).build(),
User.builder().age(2).build(),
User.builder().age(1).build(),
User.builder().age(2).build(),
User.builder().age(5).build()
).stream().collect(Collectors.groupingBy(User::getAge));
}
}
collect(Supplier<R> supplier,BiConsumer<R, ? super T> accumulator,BiConsumer<R, R> combiner);
和前面一样用于提高收集的效率,进行并发收集
如下是测试案例分别在非parallel下 和 parallel下的情况
import java.util.ArrayList;
import java.util.Arrays;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
class User {
int age;
}
/** * @author 诗水人间 * @link 博客:{https://yumbo.blog.csdn.net/} * @link github:{https://github.com/1015770492} * @date 2021/9/20 0:06 */
public class CollectDemo {
public static void main(String[] args) {
ArrayList<Object> collect;
collect = Arrays.asList(
User.builder().age(1).build(),
User.builder().age(2).build(),
User.builder().age(1).build(),
User.builder().age(2).build(),
User.builder().age(5).build()
).stream().collect(ArrayList::new, ArrayList::add, (x, y) -> {
});
// 单线程进行收集
System.out.println(collect);
// 多线程下进行收集,会发现没有收集所有元素
collect = Arrays.asList(
User.builder().age(1).build(),
User.builder().age(2).build(),
User.builder().age(1).build(),
User.builder().age(2).build(),
User.builder().age(5).build()
).stream().parallel().collect(ArrayList::new, ArrayList::add, (x, y) -> {
});
System.out.println(collect);
// 正确的并发收集
collect = Arrays.asList(
User.builder().age(1).build(),
User.builder().age(2).build(),
User.builder().age(1).build(),
User.builder().age(2).build(),
User.builder().age(5).build()
).stream().parallel().collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
System.out.println(collect);
}
}
[User(age=1), User(age=2), User(age=1), User(age=2), User(age=5)]
[User(age=1)]
[User(age=1), User(age=2), User(age=1), User(age=2), User(age=5)]
min和max都需要传入一个比较器,用于得到流中最大最小的元素。
count用于计算流中元素个数
import java.util.Arrays;
/** * @author 诗水人间 * @link 博客:{https://yumbo.blog.csdn.net/} * @link github:{https://github.com/1015770492} * @date 2021/9/20 0:48 */
public class AnyMatch {
public static void main(String[] args) {
final boolean b = Arrays.asList(1, 2, 3, 4, 5).stream().anyMatch(x -> x > 6);
final boolean b2 = Arrays.asList(1, 2, 3, 4, 5).stream().anyMatch(x -> x > 4);
System.out.println(b);// false
System.out.println(b2);// true
final boolean b3 = Arrays.asList(1, 2, 3, 4, 5).stream().allMatch(x -> x > 0);
final boolean b4 = Arrays.asList(1, 2, 3, 4, 5).stream().allMatch(x -> x > 5);
System.out.println(b3);// true
System.out.println(b4);// false
final boolean b5 = Arrays.asList(1, 2, 3, 4, 5).stream().noneMatch(x -> x > 5);
System.out.println(b5);// true
}
}
返回第一个元素
import java.util.Arrays;
import java.util.Optional;
/** * @author 诗水人间 * @link 博客:{https://yumbo.blog.csdn.net/} * @link github:{https://github.com/1015770492} * @date 2021/9/20 0:56 */
public class FindFirstDemo {
public static void main(String[] args) {
// 得到第一个偶数
final Optional<Integer> first = Arrays.asList(1, 2, 3, 4, 5).stream()
.filter(x -> x % 2 == 0)
.findFirst();
System.out.println(first.get());// 2
}
}
并发情况下可能返回不同的结果
import java.util.Arrays;
import java.util.Optional;
/** * @author 诗水人间 * @link 博客:{https://yumbo.blog.csdn.net/} * @link github:{https://github.com/1015770492} * @date 2021/9/20 0:56 */
public class FindFirstDemo {
public static void main(String[] args) {
// 得到第一个偶数
final Optional<Integer> first = Arrays.asList(1, 2, 3, 4, 5).stream()
.parallel().findAny();
System.out.println(first.get());// 数据量比较大的时候会随机返回一个,根据并行线程的最快的那个
}
}
创建一个流
静态方法
of方法是静态的方法,上面几个方法也是静态的,注意用于快速的得到一个流
of 有一个…表达式可以接收多个参数
import java.util.stream.Stream;
/** * @author 诗水人间 * @link 博客:{https://yumbo.blog.csdn.net/} * @link github:{https://github.com/1015770492} * @date 2021/9/20 0:56 */
public class FindFirstDemo {
public static void main(String[] args) {
final Stream<Integer> integerStream = Stream.of(1, 2, 3);// 快速的得到一个1,2,3流
}
}
iterate(final T seed, final UnaryOperator<T> f)
generate(Supplier<T> s)
传入一个Supply类型的函数式接口
看到方法就知道怎么使用了concat(Stream<? extends T> a, Stream<? extends T> b)
《深入解读Collectors集合收集器》
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://yumbo.blog.csdn.net/article/details/120384948
内容来源于网络,如有侵权,请联系作者删除!