写目录
Stream API的介绍
Stream API 的出现显著提升了Java对函数式编程的支持,允许开发人员使用声明式的方式处理数据集合,例如列表、数组等。同时还能有效的使用多核处理器进行并行操作,提升应用程序的性能,代码更加简洁。
为什么要引入Stream API
我们在pojo包下创建一个Person实体
private String name;
private int age;
private String country
并在主入口处创建一个Person类型的List
List<Person> people = Arrays.asList(
new Person("Noe", 45, "USA"),
new Person("Stan", 12, "JPA"),
new Person("Noe", 45, "USA"),
new Person("Stan", 12, "JPA"),
new Person("Grace", 5, "UK"),
new Person("Alex", 18, "USA"),
new Person("Albert", 91, "FR")
);
如果我们要从people中取出age大于18的人员应该怎么做呢?
常规方法是我们设置一个循环遍历people中的每一个元素在去判断它们的age
List<Person> adults = new ArrayList<>();
for (Person person : people) {
if(person.getAge()>18)
{
adults.add(person);
}
}
System.out.println(adults);
}
这种方式虽然直观,但是当操作变得复杂时,代码就会变得冗余,难以维护。有没有更加简洁的方式去实现呢,那就是使用Stream API:
List<Person> adults = people.stream()
.filter(person -> person.getAge() > 18)
.collect(Collectors.toList());
System.out.println(adults);
我们发现当我们使用Stream API后代码变得更加的简洁方便。值得一提的是Stream本身不是一种数据结构,它不会存储数据或改变数据源,他只定义对数据的处理方式。
流的操作
想要熟练掌握Stream API ,关键在于三个核心步骤:
创建流:可以通过集合、数组、I/O通道等方式创建流。例如,可以通过Collection.stream() 方法将集合转换为流,或者通过Files.lines()方法将文件的每一行作为流的元素。
中间操作:对流进行中间操作,可以对流进行过滤、映射、排序等操作,以生成一个新的流。中间操作是惰性的,只有当执行终端操作时,中间操作才会被触发执行。
终端操作:对流进行终端操作,可以对流进行计算、收集结果、打印输出等操作。终端操作会触发流的处理,产生最终的结果。常见的终端操作包括forEach()、count()、**collect()**等。
值得注意的是,执行终端操作后,流就被消费掉不能再使用。
创建流
创建流的方式有很多,对于实现了Collection接口的集合,都可以通过Stream()方法直接创建Stream流。下面展示List、数组直接通过Stream()方法创建流:
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();
stream.forEach(System.out::println);
String[] array = {
"a", "b", "c"};
Stream<String> stream = Arrays.stream(array);
stream.forEach(System.out::println);
通过Stream.of方法直接创建流:
IntStream intStream = IntStream.of(1, 2, 3);
合并两个流:
Stream<String> stream1 = Stream.of("a", "b", "c");
Stream<String> stream2 = Stream.of("d", "e", "f");
Stream<String> concat = Stream.concat(stream1, stream2);
concat.forEach(System.out::println);
通过Stream.builder() 创建流:
Stream.Builder<String> builder = Stream.builder();
builder.add("a");
builder.add("b");
if(Math.random()>0.5)
{
builder.add("c");
}
Stream<String> stream = builder.build();
///调用build就不能再add
stream.forEach(System.out::println);
文件创建流,通过Java的files类的lines方法实现。
首先我们拿到文件的路径,在进行lines方法时他会逐行读取文件的内容,每一行文本都会被当成一行字符串处理。
需要注意,通过此方式打开的文件需要进行妥善关闭,所以下述代码进行了try with resource操作。
Path path = Paths.get("file.txt");
try(Stream<String> lines = Files.lines(path)) {
lines.forEach(System.out::println);
} catch (IOException e) {
e.getStackTrace();
}
对基本数据类型 StreamAPI 提供了IntStream、LongStream 和DoubleStream。通过使用range() 和rangeClosed 等方法可以和方便的创建这些类型的流。range 左闭右开、rangeClosed 左闭右闭。也可以使用基本类型流转化为对象流通过boxed()方法。
// IntStream intStream = IntStream.range(0,4);
IntStream intStream = IntStream.rangeClosed(0,4);
Stream<Integer> boxed = intStream.boxed();
boxed.forEach(System.out::println);
创建无限流,使用Stream的generate方法创建,通过limit来限制创建的个数。
Stream<String> stringStream = Stream.generate(() -> "Mizuki").limit(5);
stringStream.forEach(System.out::println);
Stream.generate(Math::random).limit(5).forEach(System.out::println);
通过Stream的iterate方法可以实现生成数学序列或者迭代算法,下述代码实现一个起始为 0 的有界等差数列:
Stream.iterate(0,n->n+2).limit(10).forEach(