Java8新特性--Stream流操作
Posted Lucas小毛驴博客
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java8新特性--Stream流操作相关的知识,希望对你有一定的参考价值。
一.Stream的介绍
? Stream流操作是Java8 新增的重要特性,与之前学习的java.io包里的字节流和字符流是完全不同的概念,不是同一个东西。
? 这里的Stream流操作是java8针对集合操作的增强,专注于对集合的各自高效、便利、优雅的聚合操作。
? Stream不是集合元素,也不是数据结构,并不保存数据,它是有关算法和计算的,使用起来更像一个高级的迭代器,我们只需要给出需要对其流中的元素执行什么操作,Stream就会隐式的在内部进行遍历,作出相应的数据操作。
? Stream的处理是单向的,这一点和迭代器一样,数据只能遍历一次,一次过后就用完了,下一次得重头来,每次转换原有的Stream对象不会改变,而是返回一个新的Stream对象,这样就允许了操作可以是链式的。
? Stream流的使用是按照一定的步骤进行的,大概如下流程:
? 获取数据源 --> 数据处理 --> 执行操作获取想要的结果
二.Stream的获取
java.util.stream
包下有Interface Stream<T>
,这个就是java8新加入的最常用的流接口。
想要获取一个流,常用有以下几种方式:
- 所有的Collection集合都可以通过stream默认方法获取流。
- Stream接口的静态方法of可以获取数组对应的流。
import java.util.*;
import java.util.stream.Stream;
public class StreamTest {
public static void main(String[] args) {
// Collection获取流
ArrayList<String> arrayList = new ArrayList<>();
Stream<String> stream = arrayList.stream();
// Stream静态方法of获取流
String[] arr = {"zhangsan", "lisi", "wangwu"};
Stream<String> stream1 = Stream.of(arr);
// Map 不是Collection的子接口,获取流要分成对应的key和value
Map<String,String> map = new HashMap<>();
Stream<String> keyStream = map.keySet().stream();
Stream<String> valueStream = map.values().stream();
}
}
三.Stream常用的方法
Stream流操作一些常用API,可以分为两类:
- 延迟方法:返回值还是Stream接口自身类型的方法,支持链式调用。(除了终结方法,其余方法都是延迟方法)
- 终结方法:返回值不再是Stream接口自身类型的方法,因此不再支持链式调用。
3.1 终结方法
forEach
void forEach(Consumer<? super T> action)
该方法与for循环中的for-each是不同的,该方法接收一个Consumer接口函数,会将每一个流元素交给该函数进行处理,它是一个终结操作,执行之后Stream流中的数据都会被消费掉。
public class StreamTest { public static void main(String[] args) { Stream<String> stream = Stream.of("zhangsan", "lisi", "wangwu"); stream.forEach(System.out::println); } } // 输出:zhangsan lisi wangwu
count
long count()
:返回流中元素的个数。是一个终结方法public class StreamTest { public static void main(String[] args) { Stream<Integer> stream = Stream.of(11, 33, 55, 77, 99); long count = stream.count(); System.out.println(count); } } // 输出: 5
3.2 延迟方法
filter
Stream<T> filter(Predicate<? super T> predicate)
之前所学**Predicate**是函数式接口,唯一的抽象方法是**test()**,该方法会返回一个boolean值,如果为true,filter方法就会留下元素,如果为false,则会丢弃元素。
总的来说,filter就是进行数据筛选,根据条件选择想要的元素,放在一个新的Stream。
举例:挑选出偶数数字
public class StreamTest { public static void main(String[] args) { List<Integer> array = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); array.stream().filter((num) -> num % 2 == 0).forEach(System.out::println); } } // 输出:2 4 6 8 10
map
<R> Stream<R> map(Function<? super T, ? extends R> mapper)
map将流中的元素映射成另一个流中的元素,该接口需要一个Function函数式接口作为参数,可以将当前流中T类型数据转换为另一种R类型的流。
举例:将数字映射为数字的平方
public class StreamTest { public static void main(String[] args) { Stream<Integer> stream1 = Stream.of(1,2,3,4,5); stream1.map((num) -> num * num).forEach(System.out::println); } } // 输出:1 4 9 16 25
limit
Stream<T> limit(long maxSize)
limit方法可以对流进行截取,只取前几个
public class StreamTest { public static void main(String[] args) { List<Integer> arr = Arrays.asList(11,22,33,44,55); arr.stream().limit(2).forEach(System.out::println); } } // 输出:11 22
skip
Stream<T> skip(long n)
:用于跳过前几个元素,获取一个截取之后的新流。public class StreamTest { public static void main(String[] args) { List<Integer> arr = Arrays.asList(11,22,33,44,55); arr.stream().skip(2).forEach(System.out::println); } } // 输出:33 44 55
concat
static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
:用于将两个流合并成一个流,这个是Stream的静态方法,与String类中的concat方法是不同的。public class StreamTest { public static void main(String[] args) { Stream<Integer> stream = Stream.of(11, 33, 55); Stream<Integer> stream1 = Stream.of(22,44); Stream.concat(stream, stream1).forEach(System.out::println); } } // 输出: 11 33 55 22 44
四.Stream的使用
需要特别注意处理顺序,中间操作(延迟方法)的一个重要特性是惰性,没有调用终结方法时候是不会执行的。
处理顺序为什么很重要,以下例子可以看出:
Stream.of("d2", "a2", "b1", "b3", "c") .map(s-> { System.out.println("map: " + s); return s.toUpperCase(); }) .filter(s-> { System.out.println("filter: " + s); return s.startsWith("A"); }) .forEach(s-> System.out.println("forEach: " + s)); // 输出结果为: // map: d2 // filter: D2 // map: a2 // filter: A2 // forEach: A2 // map: b1 // filter: B1 // map: b3 // filter: B3 // map: c // filter: C
由结果可以看出,每个字符串都是调用了map和filter,有五个字符串,就调用了5次map和filter,而forEach只调用一次,因为经过filter过滤后满足A开头的字符串只有一个。
但当我们改变中间操作的顺序时候,是可以减少很多操作的:
Stream.of("d2", "a2", "b1", "b3", "c") .filter(s-> { System.out.println("filter: " + s); return s.startsWith("a"); }) .map(s-> { System.out.println("map: " + s); return s.toUpperCase(); }) .forEach(s-> System.out.println("forEach: " + s)); // 输出为: // filter: d2 // filter: a2 // map: a2 // forEach: A2 // filter: b1 // filter: b3 // filter: c
现在,map只被调用了一次,因此操作管道在大量元素输入时执行得更快。
在编写复杂的方法链时,必须记住操作顺序很重要!
更加详细内容可以参考文章学习:Java 8 Stream 教程
以上是关于Java8新特性--Stream流操作的主要内容,如果未能解决你的问题,请参考以下文章