一、为什么引入Stream流
流是一系列与特定存储机制无关的元素——实际上,流并没有“存储”之说。使用流,无需迭代集合中的元素,就可以从管道提取和操作元素。这些管道通常被组合在一起,形成一系列对流进行操作的管道。
在大多数情况下,将对象存储在集合中是为了处理他们,因此你将会发现你将编程的主要焦点从集合转移到了流上,流的一个核心的好处是,它使得程序更加短小并且更易理解。当Lambda表达式和方法引用和流一起使用的时候会让人感觉自成一体。
二、如何使用Stream流流操作的类型有三种:创建流,修改流元素(中间操作 Intermediate Operations),消费流元素(终端操作 Terminal Operations)
创建Stream流
使用Arrays.stream()方法创建
Integer[] arr = new Integer[]{1,2,3,4,5}; Arrays.stream(arr).filter(num -> num > 3);
使用Stream.of ()方法创建
Integer[] arr = new Integer[]{1,2,3,4,5}; Stream.of(arr).filter(num -> num > 3);查看of()的源码中得知,该方法也是调用了Arrays.stream()方法实现的
/** * Returns a sequential ordered stream whose elements are the specified values. * * @param <T> the type of stream elements * @param values the elements of the new stream * @return the new stream */ @SafeVarargs @SuppressWarnings("varargs") // Creating a stream from an array is safe public static<T> Stream<T> of(T... values) { return Arrays.stream(values); }
使用Collection.stream()方法创建
List<String> list = new ArrayList<>(1); list.stream().forEach(str -> System.out.println(str));
使用Stream.iterate()方法创建
Stream.iterate(1, num -> num + 2).limit(10).forEach(num -> System.out.println(num));
使用Stream.generate()方法创建
Stream.generate(() -> Arrays.asList(arr)).limit(1).forEach(num -> System.out.println(num));修改流元素(中间操作 Intermediate Operations)
中间操作用于从一个流中获取对象,并将对象作为另一个流从后端输出,以连接到其他操作。
1、跟踪和调试peek() 操作的目的是帮助调试,允许你无修改地查看流中的元素
// streams/Peeking.java class Peeking { public static void main(String[] args) throws Exception { FileToWords.stream("Cheese.dat") .skip(21) .limit(4) .map(w -> w + " ") .peek(System.out::print) .map(String::toUpperCase) .peek(System.out::print) .map(String::toLowerCase) .forEach(System.out::print); } }输出结果:
Well WELL well it IT it s S s so SO so因为 peek() 符合无返回值的 Consumer 函数式接口,所以我们只能观察,无法使用不同的元素来替换流中的对象。
2、流元素排序sorted()方法是需要遍历整个流的,并在产生任何元素之前对它进行排序。因为有可能排序后集合的第一个元素会在未排序集合的最后一位。
@Test public void sortedTest() { List<Integer> numList = Lists.newArrayList(); numList.add(8); numList.add(2); numList.add(6); numList.add(9); numList.add(1); List<Integer> sortList = numList.stream().sorted(Integer::compareTo).collect(Collectors.toList()); System.out.println(sortList); }输出结果:
[1, 2, 6, 8, 9] 3、移除元素
distinct() 可用于消除流中的重复元素。相比创建一个 Set 集合,该方法的工作量要少得多。
@Test public void distinctTest() { Stream.of(6, 8, 9, 6, 2, 8).distinct().forEach(i -> System.out.print(i + ", ")); }输出结果:
6, 8, 9, 2,
filter(Predicate):若元素传递给过滤函数产生的结果为true ,则过滤操作保留这些元素。
@Test public void filterTest() { Stream.of(6, 9, 2, 8).filter(num -> num > 5).sorted().forEach(i -> System.out.print(i + ", ")); }输出结果:
6, 8, 9, 4、映射,应用函数到元素
map(Function):将函数操作应用在输入流的元素中,对一个流中的值进行某种形式的转换,并将返回值传递到输出流中
@Test public void mapTest() { Stream.of("abc", "qw", "mnkh").map(String::length).forEach(n -> System.out.format("%d ", n)); }输出结果:
3 2 4
mapToInt(ToIntFunction):操作同上,但结果是 IntStream
Stream.of("5", "7", "9").mapToInt(Integer::parseInt).forEach(n -> System.out.format("%d ", n));
mapToLong(ToLongFunction):操作同上,但结果是 LongStream
Stream.of("17", "19", "23").mapToLong(Long::parseLong).forEach(n -> System.out.format("%d ", n));
mapToDouble(ToDoubleFunction):操作同上,但结果是 DoubleStream
Stream.of("17", "1.9", ".23").mapToDouble(Double::parseDouble).forEach(n -> System.out.format("%f ", n));
flatMap() 做了两件事:将产生流的函数应用在每个元素上(与 map() 所做的相同),然后将每个流都扁平化为元素,因而最终产生的仅仅是元素。
List<Integer> listA = Lists.newArrayList(); listA.add(1); listA.add(6); List<Integer> listB = Lists.newArrayList(); listB.add(10); listB.add(2); Map<String, List<Integer>> abMap = Maps.newHashMap(); abMap.put("A", listA); abMap.put("B", listB); // 需获取A和B集合中大于5的元素 abMap.values().stream().flatMap(num -> num.stream().filter(n -> n > 5)).collect(Collectors.toList()) .forEach(System.out::println);输出结果:
6 10
flatMapToInt(Function):当 Function 产生 IntStream 时使用。
flatMapToLong(Function):当 Function 产生 LongStream 时使用。
flatMapToDouble(Function):当 Function 产生 DoubleStream 时使用。
5、集合流切片,可实现分页
limit(n)方法会返回一个包含n个元素的新的流(若总长小于n则返回原始流)。