Stream是JDK1.8新增的一个特性,它是一个位于【java.util.stream】包下面的接口,接口中定义了用于操作数据的公共方法,Stream被称作:【流】,它的意图就是可以按照流的方式对数据进行一些加工处理,实际开发里面最常见的就是操作集合数据,通过Stream API可以编写很少的代码就完成某一个功能,相比于传统的集合处理方式,Stream API显示更加的优雅、高效。
Java中可以将Stream API方法大致分为两类,分别是:【中间方法】和【终端方法】。
- 中间方法:在Stream流被消费之前,对Stream流中的数据进行一些加工处理,然后返回加工处理之后的新的Stream流。
- 终端方法:Stream流经过中间方法的加工处理之后,最终是需要被消费的,调用终端方法之后,这个Stream流就结束了,之后就不能够在使用这个Stream流对象。
Stream流处理大致流程图:
下面详细介绍一下Stream API的常见方法。
Java中主要有下面四种方法创建一个Stream对象,分别如下所示:
- 第一种:使用Stream提供的empty()方法。
- 第二种:使用Stream提供的of()方法。
- 第三种:使用Arrays.stream()数组工具类提供的方法。
- 第四种:使用集合对象的stream()方法(这种方式在实际开发里面最常用)。
Stream接口中提供了一个empty()方法,该方法用于创建一个空的Stream对象,基本上不会用这个方法吧,反正我是没用过-。
public class StreamDemo {
public static void main(String[] args) {
// 创建空的 Stream 对象
Stream<Object> empty = Stream.empty();
}
}
Stream接口中提供了两个of()方法,of()方法可以将传入的数据转换为一个Stream对象返回。
public class StreamDemo {
public static void main(String[] args) {
// 创建 Stream 对象
Stream<String> stream = Stream.of("123");
// 可变参数
Stream<Integer> stream1 = Stream.of(1, 2, 3, 4);
}
}
Arrays数组工具类中提供了一个stream()方法,该方法可以将数组转换为Stream流对象。
public class StreamDemo {
public static void main(String[] args) {
// 创建 Stream 对象
int[] arr = new int[] {1, 2, 3, 4, 5};
IntStream stream = Arrays.stream(arr);
}
}
Java集合类中提供了一个stream()方法,作用也是将Collection集合数据转换为Stream流对象。
public class StreamDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("111");
list.add("222");
list.add("333");
// 创建 Stream 对象
Stream<String> stream = list.stream();
}
}
注意:只要是Collection集合类,它都具有stream()方法。并且这个也是最常用的方法,因为实际开发里面,集合是最常见的操作对象。
Stream中间方法是对Stream流中数据进行一些加工处理的,中间方法的返回值都是一个新的Stream流对象,这个Stream流对象就是加工处理之后的新对象。
filter()方法用于过滤Stream流中的数据,该方法需要传递一个【Predicate】对象(一个函数式接口),可以采用lambada表达式编写过滤条件。
public class StreamDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("111");
list.add("222");
list.add("333");
// 创建 Stream 对象
Stream<String> stream = list.stream();
// 过滤方法: 过滤出集合中不等于 222 的数据
Stream<String> stream1 = stream.filter(item -> !item.equals("222"));
}
}
map()方法主要作用就是将一个Stream流映射成一个新的Stream流,map方法使用最为频繁,比如:从集合中提取某个对象的某个属性,将其组成新的一个集合,下面看下两种实现方式。
public static void main(String[] args) {
List<User> userList = getUserList();
// 从集合中提取 姓名
List<String> nameList = new ArrayList<>();
for (int i = 0; i < userList.size(); i++) {
nameList.add(userList.get(i).getName());
}
}
public static void main(String[] args) {
List<User> userList = getUserList();
// 从集合中提取 姓名
List<String> nameList = userList.stream().map(item -> item.getName()).collect(Collectors.toList());
}
对比上面两种方式,可以看到,采用Stream API编写的代码是多么的优雅,并且代码也简化了很多。
flatMap()方法和map()方法的作用是类似的,都是用于映射Stream流的,只不过flatMap()方法可以将多个不同的Stream流映射为一个Stream流对象,flat表示【扁平化】的意思,就好比将多行数据合并成一行数据,这个就是扁平化。
public class StreamDemo {
public static List<List<User>> getUserList() {
List<List<User>> ans = new ArrayList<>();
List<User> list = new ArrayList<>();
list.add(new User(333, "name_003", "pass_003"));
list.add(new User(111, "name_001", "pass_001"));
list.add(new User(444, "name_004", "pass_004"));
ans.add(list);
List<User> list2 = new ArrayList<>();
list2.add(new User(222, "name_002", "pass_002"));
ans.add(list2);
return ans;
}
public static void main(String[] args) {
List<List<User>> userList = getUserList();
System.out.println(userList);
// 将集合中的集合,转换为一个集合
List<User> ansList = userList.stream().flatMap(item -> item.stream()).collect(Collectors.toList());
System.out.println(ansList);
}
}
上面案例中,由于集合里面的元素又是集合,为了将整个集合变成一个集合,通过Stream中的flatMap()方法,将多个Stream流映射成一个Stream对象。
limit()方法作用是限定数据的条数,比如:集合中有100条数据,通过limit()方法可以只获取10条数据。
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("111");
list.add("222");
list.add("333");
list.add("444");
list.add("555");
// 限定5条数据
Stream<String> stream = list.stream().limit(5);
}
skip()方法作用是:跳过多少条数据,然后在开始处理数据。比如:现在有100条数据,前10条数据是不需要的,从第11条数据开始进行处理,这个时候就可以通过skip()方法跳过前10条数据进行处理。
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("111");
list.add("222");
list.add("333");
list.add("444");
list.add("555");
// 跳过前 2 条数据
Stream<String> skip = list.stream().skip(2);
// 打印结果
skip.forEach(System.out::println);
}
打印结果如下所示:
sorted()方法作用是将数据进行排序,sorted()有两个方法,一个是无参的sorted()方法,另一个是有参数的sorted(Comparator)方法。
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("333");
list.add("111");
list.add("444");
list.add("222");
list.add("555");
System.out.println("排序前: ");
list.forEach(item -> System.out.print(item + " "));
System.out.println();
// 排序
Stream<String> sorted = list.stream().sorted();
System.out.println("排序后: ");
sorted.forEach(item -> System.out.print(item + " "));
}
运行结果如下所示:
public class StreamDemo {
public static List<User> getUserList() {
List<User> list = new ArrayList<>();
list.add(new User(333, "name_003", "pass_003"));
list.add(new User(111, "name_001", "pass_001"));
list.add(new User(444, "name_004", "pass_004"));
list.add(new User(222, "name_002", "pass_002"));
return list;
}
public static void main(String[] args) {
List<User> userList = getUserList();
System.out.println("排序前: ");
userList.forEach(System.out::println);
// 自定义排序
Stream<User> sorted = userList.stream().sorted(new Comparator<User>() {
@Override
public int compare(User o1, User o2) {
return o1.getId() - o2.getId();
}
});
System.out.println("排序后: ");
sorted.forEach(System.out::println);
}
}
运行结果如下所示:
distinct()方法作用是将数据中的重复数据去重操作,只保留一条数据。
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("111");
list.add("222");
list.add("333");
list.add("444");
// 重复数据
list.add("555");
list.add("555");
// 去重
Stream<String> distinct = list.stream().distinct();
// 打印结果
distinct.forEach(System.out::println);
}
打印结果如下所示:
peek()方法接收一个Consumer消费者对象,消费者可以对流中的数据进行修改操作或者访问操作。
public class StreamDemo {
public static List<User> getUserList() {
List<User> list = new ArrayList<>();
list.add(new User(333, "name_003", "pass_003"));
list.add(new User(111, "name_001", "pass_001"));
list.add(new User(444, "name_004", "pass_004"));
list.add(new User(222, "name_002", "pass_002"));
return list;
}
public static void main(String[] args) {
List<User> userList = getUserList();
// peek 修改数据
Stream<User> peek = userList.stream().peek(item -> item.setName("demo_peek"));
peek.forEach(System.out::println);
}
}
注意:Stream中间方法的执行操作并不会立即生效,只有调用终端方法之后,中间方法的执行效果才会生效。
终端方法是将Stream流消费之后,并且最终会得到一个结果,常见的终端方法下面这些。
min()方法主要作用:从数据流中计算出最小值元素。min()方法需要提供一个Comparator比较器对象,通过比较器器对象的比较规则,找出最小值元素。
注意:min()方法不是说找出最小的元素,它是根据你指定的比较器规则找出最小元素。
比如下面这个例子,集合元素是【1,2,3,4,5】,但是比较器规则是后一个元素减去前一个元素。
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> min = list.stream().min(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
Integer ans = min.get();
System.out.println(ans);
}
上面输出结果是:【5】。
max()方法作用:从数据流中计算出最小值元素。参数和min()方法是相同的。
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> max = list.stream().max(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
Integer ans = max.get();
System.out.println(ans);
}
上面输出结果是:【1】。
count()方法作用:统计Stream流中数据元素个数,返回值是long类型。
public static void main(String[] args) {
List<Integer> list = Arrays.asList(2, 1, 5, 4, 3);
// 计算个数
long count = list.stream().count();
System.out.println(count);
}
reduce()方法作用:将stream流中数据进行规约操作,最终得到一个结果。这里的规约就类似于迭代,比如:要计算stream流中所有数据的和,那就可以使用reduce()方法进行规约计算。
public static void main(String[] args) {
List<Integer> list = Arrays.asList(2, 1, 5, 4, 3);
// 规约操作
Optional<Integer> ans = list.stream().reduce((a, b) -> a + b);
System.out.println(ans.get());
// 指定初始值:
// 这里指定第一个参数值是10,也就是reduce的规约操作的初始值是10
int ans1 = list.stream().reduce(10, (a, b) -> a + b);
System.out.println(ans1);
// 这里结果等于:1200
int ans2 = list.stream().reduce(10, (a, b) -> a * b);
System.out.println(ans2);
}
运行结果如下所示:
reduce()规约操作分析:
anyMatch()方法作用:只要stream数据流中 【存在一个满足匹配】 条件的数据,那么方法就会返回true,否则返回false。
allMatch()方法作用:只有当stream数据流中的所有数据,【都满足匹配】 条件的数据,那么方法才会返回true,否则返回false。
allMatch()方法作用:只有当stream数据流中的所有数据,【都不满足匹配】 条件的数据,那么方法才会返回true,否则返回false。
public static void main(String[] args) {
List<Integer> list = Arrays.asList(2, 1, 5, 4, 3);
// 满足一个: 集合中有一个元素是大于4的
boolean match = list.stream().anyMatch(item -> item > 4);
System.out.println(match);
// 都满足: 集合中所有元素都小于6
boolean match1 = list.stream().allMatch(item -> item < 6);
System.out.println(match1);
// 都不满足: 集合中不存在大于 6 的元素
boolean match2 = list.stream().noneMatch(item -> item > 6);
System.out.println(match2);
}
findFirst()方法作用:从stream流中获取第一条数据。
public class StreamDemo {
public static List<User> getUserList() {
List<User> list = new ArrayList<>();
list.add(new User(333, "name_003", "pass_003"));
list.add(new User(111, "name_001", "pass_001"));
list.add(new User(444, "name_004", "pass_004"));
list.add(new User(222, "name_002", "pass_002"));
return list;
}
public static void main(String[] args) {
List<User> userList = getUserList();
// 获取第一条数据
Optional<User> first = userList.stream().findFirst();
if (first.isPresent()) {
System.out.println(first.get());
}
}
}
findAny()方法作用:从stream数据流中随机返回一条数据。
forEach()方法作用:遍历stream流中的数据元素。
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("333");
list.add("111");
list.add("444");
list.add("222");
list.add("555");
list.stream().forEach(item -> {
System.out.println(item);
});
}
collect()方法作用:用于将中间方法处理的stream流收集为一个结果,常见的如:List、Set、Map等等。
public class StreamDemo {
public static List<User> getUserList() {
List<User> list = new ArrayList<>();
list.add(new User(333, "name_003", "pass_003"));
list.add(new User(111, "name_001", "pass_001"));
list.add(new User(444, "name_004", "pass_004"));
list.add(new User(222, "name_002", "pass_002"));
return list;
}
public static void main(String[] args) {
List<User> userList = getUserList();
List<String> ansList = userList.stream().map(User::getName).collect(Collectors.toList());
System.out.println(ansList);
}
}
这个collect()方法更多的用法上一篇文章Java8新特性有介绍。