写点什么

Java- 技术专题 -JDK8 新特性之 Stream 流

发布于: 2020 年 10 月 30 日
Java-技术专题-JDK8新特性之Stream流

Stream流式思想概述

注意:Stream和IO流(InputStream/OutputStream)没有任何关系,请暂时忘记对传统IO流的固有印象!

Stream流式思想类似于工厂车间的“生产流水线”,Stream流不是一种数据结构,不保存数据,而是对数据进行加工处理。Stream可以看作是流水线上的一个工序。在流水线上,通过多个工序让一个原材料加工成一个商品。



Stream API能让我们快速完成许多复杂的操作,如筛选、切片、映射、查找、去除重复,统计,匹配和归约;

获取Stream流的两种方式

java.util.stream.Stream 是JDK 8新加入的流接口;

获取一个流非常简单,有以下几种常用的方式:

  1. 所有的 Collection 集合都可以通过 stream 默认方法获取流;

  2. Stream 接口的静态方法 of 可以获取数组对应的流;

根据Collection获取流:

java.util.Collection 接口中加入了default方法 stream 用来获取流,所以其所有实现类均可获取流:

public interface Collection {
default Stream<E> stream()
}



import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import java.util.stream.Stream;
public class Test {
public static void main(String[] args) {
// 集合获取流
// Collection接口中的方法: default Stream<E> stream() 获取流
List<String> list = new ArrayList<>();
Stream<String> stream1 = list.stream();
//Set获取流
Set<String> set = new HashSet<>();
Stream<String> stream2 = set.stream();
//Vector获取流
Vector<String> vector = new Vector<>();
Stream<String> stream3 = vector.stream();
//java.util.Map 接口不是 Collection 的子接口,所以获取对应的流需要分key、value或entry等情况:
Map<String, String> map = new HashMap<>();
Stream<String> keyStream = map.keySet().stream();
Stream<String> valueStream = map.values().stream();
Stream<Map.Entry<String, String>> entryStream = map.entrySet().stream();
}
}

Stream中的静态方法of获取流

由于数组对象不可能添加默认方法,所以 Stream 接口中提供了静态方法 of ,使用很简单:

import java.util.stream.Stream;

public class Test {

public static void main(String[] args) {

// Stream中的静态方法: static Stream of(T... values)

Stream<String> stream6 = Stream.of("aa", "bb", "cc");

String[] arr = {"aa", "bb", "cc"};

Stream<String> stream7 = Stream.of(arr);

Integer[] arr2 = {11, 22, 33};

Stream<Integer> stream8 = Stream.of(arr2);

// 注意:基本数据类型的数组不行

int[] arr3 = {11, 22, 33};

Stream<int[]> stream9 = Stream.of(arr3);

//会报错

Integer[] arr4 = {11, 22, 33};

Stream<int[]> stream10 = Stream.of(arr4);

}

}

Stream常用方法

Stream流模型的操作很丰富,这里介绍一些常用的API。这些方法可以被分成两种:

  1. 终结方法:返回值类型不再是 Stream 类型的方法,不再支持链式调用。本小节中,终结方法包括 count 和forEach 方法;

  2. 非终结方法:返回值类型仍然是 Stream 类型的方法,支持链式调用。(除了终结方法外,其余方法均为非终结方法);

Stream注意事项(重要)

  1. Stream只能操作一次;

  2. Stream方法返回的是新的流;

  3. Stream不调用终结方法,中间的操作不会执行

Stream流的forEach方法

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Test {
public static void main(String[] args) {
List<String> one = new ArrayList<>();
Collections.addAll(one, "迪丽热巴", "宋远桥", "苏星河", "老子", "庄子", "孙子");
//Lambda表达式
one.stream().forEach((String s) ->{
System.out.println(s);
});
// 简写
one.stream().forEach(s -> System.out.println(s));
//方法引用
one.stream().forEach(System.out::println);
}
}
//控制台输出:
迪丽热巴
宋远桥
苏星河
老子
庄子
孙子

Stream流的count方法

Stream流提供 count 方法来统计其中的元素个数:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Test {
public static void main(String[] args) {
List<String> one = new ArrayList<>();
Collections.addAll(one, "迪丽热巴", "宋远桥", "苏星河", "老子", "庄子", "孙子");
System.out.println(one.stream().count());
}
}
//控制台输出:6

Stream流的filter方法

filter用于过滤数据,返回符合过滤条件的数据:





可以通过 filter 方法将一个流转换成另一个子集流。方法声明:

该接口接收一个 Predicate 函数式接口参数(可以是一个Lambda或方法引用)作为筛选条件。Stream流中的 filter 方法基本使用的代码如:



import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Test {
public static void main(String[] args) {
List<String> one = new ArrayList<>();
Collections.addAll(one, "迪丽热巴", "宋远桥", "苏星河", "老子", "庄子", "孙子");
one.stream().filter(s -> s.length() == 2).forEach(System.out::println);
}
}
//控制台输出:
老子
庄子
孙子

在这里通过Lambda表达式来指定了筛选的条件:姓名长度为2个字。

Stream流的limit方法





limit 方法可以对流进行截取,只取用前n个。方法签名:

import java.util.ArrayList;

import java.util.Collections;

import java.util.List;

public class Test {

public static void main(String[] args) {

List<String> one = new ArrayList<>();

Collections.addAll(one, "迪丽热巴", "宋远桥", "苏星河", "老子", "庄子", "孙子");

one.stream().limit(3).forEach(System.out::println);

}
}

//控制台输出:

迪丽热巴

宋远桥

苏星河

Stream流的skip方法





如果希望跳过前几个元素,可以使用 skip 方法获取一个截取之后的新流:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Test {
public static void main(String[] args) {
List<String> one = new ArrayList<>();
Collections.addAll(one, "迪丽热巴", "宋远桥", "苏星河", "老子", "庄子", "孙子");
one.stream().skip(5).forEach(System.out::println);
}
}
//控制台输出:孙子

Stream流的map方法





如果需要将流中的元素映射到另一个流中,可以使用 map 方法。方法签名:

import java.util.stream.Stream;

public class Test {
public static void main(String[] args) {
Stream<String> original = Stream.of("11", "22", "33");
Stream<Integer> result = original.map(Integer::parseInt);
result.forEach(s -> System.out.println(s + 10));
}
}
//控制台打印:
21
32
43

Stream流的sorted方法

如果需要将数据排序,可以使用 sorted 方法。方法签名:

import java.util.stream.Stream;
public class Test {
public static void main(String[] args) {
// sorted(): 根据元素的自然顺序排序
// sorted(Comparator<? super T> comparator): 根据比较器指定的规则排序
Stream.of(33, 22, 11, 55)
.sorted()
.sorted((o1, o2) -> o2 - o1)
.forEach(System.out::println);
}
}
//控制台打印:55 33 22 11

sorted 方法根据元素的自然顺序排序,也可以指定比较器排序;



Stream流的distinct方法





如果需要去除重复数据,可以使用 distinct 方法。方法签名:

import java.util.stream.Stream;

public class Test {
public static void main(String[] args) {
Stream.of(
new Person("刘德华", 58),
new Person("张学友", 56),
new Person("张学友", 56),
new Person("黎明", 52))
.distinct()
.forEach(System.out::println);
}
}
//控制台打印:
Person@448139f0
Person@7cca494b
Person@7ba4f24f
Person@3b9a45b3

并没有清除,自定义类型是根据对象的hashCode和equals来去除重复元素的,重写Person类的equals()与hashCode():

Stream流的find方法





如果需要找到某些数据,可以使用 find 相关方法。方法签名:

import java.util.Optional;
import java.util.stream.Stream;

public class Test {
public static void main(String[] args) {
Optional<Integer> first = Stream.of(5, 3, 6, 1).findFirst();
System.out.println("first = " + first.get());
Optional<Integer> any = Stream.of(5, 3, 6, 1).findAny(); //默认找第一个
System.out.println("any = " + any.get());
}
}
//控制台打印:
first = 5
any = 5

Stream流的max和min方法

import java.util.Optional;

import java.util.stream.Stream;

public class Test {

public static void main(String[] args) {

Optional<Integer> max = Stream.of(5, 3, 6, 1).max((o1, o2) -> o1 - o2);

System.out.println("first = " + max.get());

Optional<Integer> min = Stream.of(5, 3, 6, 1).min((o1, o2) -> o1 - o2);

System.out.println("any = " + min.get());

}

}

//控制台打印:

first = 6

any = 1

Stream流的reduce方法

Stream流中的 reduce 相关方法基本使用的代码如:

import java.util.stream.Stream;
public class Test {
public static void main(String[] args) {
//Lambda表达式
int reduce = Stream.of(4, 5, 3, 9)
.reduce(0, (a, b) -> {
System.out.println("a = " + a + ", b = " + b);
return a + b;
});
// reduce:
// 第一次将默认做赋值给x, 取出第一个元素赋值给y,进行操作
// 第二次,将第一次的结果赋值给x, 取出二个元素赋值给y,进行操作
// 第三次,将第二次的结果赋值给x, 取出三个元素赋值给y,进行操作
// 第四次,将第三次的结果赋值给x, 取出四个元素赋值给y,进行操作
System.out.println("reduce = " + reduce);
//Lambda表达式简化
int reduce2 = Stream.of(4, 5, 3, 9)
.reduce(0, (x, y) -> {
return Integer.sum(x, y);
});
//方法引用
int reduce3 = Stream.of(4, 5, 3, 9).reduce(0, Integer::sum);
int max = Stream.of(4, 5, 3, 9)
.reduce(0, (x, y) -> {
return x > y ? x : y;
});
System.out.println("max = " + max);

}
}
//控制台输出:
a = 0, b = 4
a = 4, b = 5
a = 9, b = 3
a = 12, b = 9
reduce = 21
max = 9

Stream流的concat方法

如果有两个流,希望合并成为一个流,那么可以使用 Stream 接口的静态方法 concat :

import java.util.stream.Stream;
public class Test {
public static void main(String[] args) {
Stream<String> streamA = Stream.of("张三");
Stream<String> streamB = Stream.of("李四");
Stream<String> result = Stream.concat(streamA, streamB);
result.forEach(System.out::println);
Long count = streamA.count();
System.out.println(count);
}
}



用户头像

我们始于迷惘,终于更高的迷惘. 2020.03.25 加入

一个酷爱计算机技术、健身运动、悬疑推理的极客狂人,大力推荐安利Java官方文档:https://docs.oracle.com/javase/specs/index.html

评论

发布
暂无评论
Java-技术专题-JDK8新特性之Stream流