Java8新特性探索之Stream接口

一、为什么引入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则返回原始流)。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzfddd.html