jdk 8新特性之 Stream 流的使用
这两天没有需求,在读项目中的代码,看到了有些用Stream流来操作集合的写法,特地学习总结,记录到博客里。首先是对stream流的介绍:
1、stream 流的介绍
Stream(流)是一个来自数据源的元素队列,元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。数据源:流的来源,可以是集合,数组 等。
和以前的Collection操作不同, Stream操作还有两个基础的特征:
Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道(pipline), 如同流式风格(fluentstyle)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
内部迭代: 以前对集合遍历都是通过Iterator或者增强for的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式,流可以直接调用遍历方法。
出自:https://zhuanlan.zhihu.com/p/265884828
从上可以看出,流操作可以便利的进行迭代操作,对集合进行处理,那么该如何获得流呢,针对不同的类型,一般情况下有三种方式可以获得stream流,
2、如何获得一个stream流
如果需要构建的stream的集合来源于继承了collection类的类,如List,那么可以使用XXX.stream() 直接构建流。
// 1、通过集合Collection获取 //extends Collection<E> 的集合类型都可以通过该方法获取到 List<Integer> list = Arrays.asList(1, 2, 4, 5, 6, 3, 9, 1); Stream<Integer> stream = list.stream();
如果需要构建stream的集合来源于数组,如String[] ,那么可以使用Arrays.stream(xxx) 可以得到stream流
// 2、通过数组获取 String[] arr = {"are", "you", "ok?"}; Stream<String> stream1 = Arrays.stream(arr); int[] numArr = new int[]{1, 2, 5, 6, 3, 1}; // 需要装箱操作,否则返回IntStream Stream<Integer> boxed = Arrays.stream(numArr).boxed();
如果需要构建stream的集合只有值,那么可以使用stream.of(xxx,xxx,xxx)直接构建
// 3、通过值直接构建流 Stream<String> stringStream = Stream.of("are", "you", "ok");
3、常用的stream操作
stream其基本思想是基于一次迭代尽可能多的对元素进行操作,设计并实现的,stream操作可以分为以下几类,分别为中间操作以及结束操作,不同操作的特征分类与特征如下所示:
filter 实现对元素的过滤,传入返回类型为boolean的lambda表达式
// 常用管道操作 filter 接受一个lambda 表达式 返回类型为boolean // 流执行时,每个元素逐一进行筛选过滤,只有满足条件的才会到下一步 List<String> collect = stringStream.filter(x -> !x.isEmpty()) .filter(x -> x.length() > 2) .collect(Collectors.toList()); collect.forEach( x -> System.out.println(x) );
distinct 去重,无传入
// 去重 distinct List<Integer> collect1 = stream.distinct().collect(Collectors.toList()); for (Integer integer : collect1) { System.out.println(integer); }
skip limited 截取元素,使用limited截取前若干个元素进入下一步;使用skip则是跳过若干个元素,进入下一步
// 截取元素 limited List<String> collect2 = stream1.limit(1).collect(Collectors.toList()); for (String s : collect2) { System.out.println(s); } // 截取元素 skip List<Integer> collect3 = boxed.skip(1).collect(Collectors.toList()); for (Integer s : collect3) { System.out.println(s); }
map 映射,可以实现数据类型的转换,并执行下一步操作,或者直接输出
// 映射 map 执行操作后转化为另外一种数据类型进行输出 Stream<Integer> boxed1 = Arrays.stream(numArr).boxed(); List<String> collect4 = boxed1.map(x -> String.valueOf(++x)).collect(Collectors.toList()); collect4.forEach(x -> System.out.println(x));
flatmap 实现多个流的合并
// 合并多个流 flatMap List<Integer> integers1 = Lists.newArrayList(43, 2, 4, 5, 7); List<Integer> integers2 = Lists.newArrayList(2, 42, 4, 11, 1); List<List<Integer>> es = new ArrayList<>(); es.add(integers1); es.add(integers2); List<Integer> collect5 = es.stream().flatMap(List::stream) .distinct() .sorted() .collect(Collectors.toList());
collect5.forEach(x -> System.out.println(x));
匹配元素 allMatch anyMatch noneMatch 全部满足,任意一个满足,全不满足
需要传入boolean类型的lambda表达式
// 匹配元素 anyMatch // 匹配元素 allMatch // 匹配元素 noneMatch Stream<Integer> integerStream = es.stream().flatMap(List::stream); Stream<Integer> integerStream1 = es.stream().flatMap(List::stream); Stream<Integer> integerStream2 = es.stream().flatMap(List::stream); boolean b1 = integerStream.anyMatch(x -> x > 0); boolean b2 = integerStream1.allMatch(x -> x > 5); boolean b3 = integerStream2.noneMatch(x -> x < 0); System.out.println(b1); System.out.println(b2); System.out.println(b3);
获取元素 findAny findFirst 前者返回任意一个满足要求的元素,需要传入返回类型为boolean的lambda表达式,findFirst 则是返回第一个元素
// 获取元素 findAny // 获取元素 findFirst // 保证不是顺序流,返回才会随机 Stream<Integer> parallel = Stream.of(2, 1, 3, 4).parallel(); Optional<Integer> any = parallel.findAny(); System.out.println("findAny测试,100次"); // https://blog.csdn.net/qq_31635851/article/details/111178278 for (int i = 0; i < 100; i++) { Stream.of(10, 20, 30).parallel().findAny() .ifPresent(s -> System.out.println(s)); } boolean present = any.isPresent(); if (present) { Integer integer = any.get(); System.out.println(integer); } Stream<Integer> integerStream3 = es.stream().flatMap(List::stream); Optional<Integer> first = integerStream3.findFirst(); if (first.isPresent()) { System.out.println(first.get()); }
注:当操作的流不是并行流,会导致findAny每次返回的都是同一个元素,需要转化为并行流,才会随机返回元素。
Stream流转换为其他数据类型 如Collection Array String
//普通转换 Stream<String> stringStream1 = Stream.of("are", "you", "ok"); Object[] array1 = stringStream1.toArray(); for (Object s : array1) { System.out.println(s.toString()); } //涉及拆箱、装箱操作的转换 Stream<Integer> stream12 = Stream.of(1, 2, 3, 4, 5, 6); int[] array12 = stream12.mapToInt(x -> x).toArray(); // Integer[] array13 = stream12.toArray(Integer[]::new); for (Integer integer : array12) { System.out.println(integer); } //将 List<Inetegr> 转化为 String[] List<Integer> list12 = Arrays.asList(1, 2, 3, 4, 5); String[] array = list12.stream().map(String::valueOf).toArray(String[]::new); for (String s : array) { System.out.println(s); } // Stream -> List List<Integer> list1 = stream.collect(Collectors.toList()); List<Integer> list2 = stream.collect(Collectors.toCollection(ArrayList::new)); // Stream ->Set Set<Integer> set = stream.collect(Collectors.toCollection(HashSet::new)); // Stream -> stack Stack<Integer> stack = stream.collect(Collectors.toCollection(Stack::new)); // Stream ->map Map<Integer, String> map = Stream.of("are", "you", "ok") .collect(Collectors.toMap(s -> s.hashCode(), s -> s)); // Stream -> String //将 List 转化为使用 “,” 分隔的字符串 List<Integer> list3 = Arrays.asList(1, 2, 3, 4, 5, 6, 7); String str = list3.stream().map(x -> x.toString()).collect(Collectors.joining(",")); System.out.println(str);
注:代码中出现的 :: 为lambda表达式引用类构造的方法的便捷写法,等价于 new 一个对应的对象。