Stream 流的使用
1,Stream 常用的 Api
1.1 筛选和切片:
List<String> excapleStrs = Arrays.asList("java", "java", "javaScript", "php","python","ios", "go");
filter: 筛选
Streams 接口支持 filter 方法(你现在应该很熟悉了)。该操作会接受一个谓词(一个返回boolean 的函数)作为参数,并返回一个包括所有符合谓词的元素的流 .
筛选长度大于四的字符;
List<String> collect =
excapleStrs
.stream().filter(x -> x.length() > 4).collect(Collectors.toList());
结果为: ["javaScript", "python"]
distinct: 去重
流还支持一个叫作 distinct 的方法,它会返回一个元素各异(根据流所生成元素的
hashCode 和 equals 方法实现)的流,,去重。
excapleStrs .stream().distinct().forEach(x->System.out.println(x));
结果为: ["java", "javaScript", "php","python","ios", "go" ]
limit: 截断
List<String> limit =excapleStrs.stream().limit(3).collect(Collectors.toList());、
结果为: ["java", "java", "javaScript"]
skip: 跳过
List<String> skip =excapleStrs
.stream().skip(3).collect(Collectors.toList());结果为: ["php","python","ios", "go"]
1.2 映射:
它会接受一个函数作为参数。这个函数会被应用到每个元素上,并将其映射成一个新的元素(使用映射一词,是因为它和转换类似,但其中的细微差别在于它是“创建一map :映射个新版本”而不是去“修改”)。
比如我们需要获取excapleStrs 每个字符的长度。
List<Integer> collect1 = strs.stream().map(String::length).collect(Collectors.toList());//方法引用
List<Integer> collect1 = strs.stream().map(x -> x.length()).collect(Collectors.toList());//lambada表达式flatMap: 多个流映射成一个。
你可以使用两个 map 来迭代这两个列表,并生成数对。但这样会返回一个 Stream <Stream<Integer[]>> 。你需要让生成的流扁平化,以得到一个 Stream<Integer[]> 。这 正是 flatMap 所做的。
// 交叉组合的方式 flatMap 组合多个Stream成为一个。 List<Integer> numbers1 = Arrays.asList(1, 2, 3); List<Integer> numbers2 = Arrays.asList(3, 4); List<int[]> pairs = numbers1.stream() .flatMap( i -> numbers2.stream().map(j -> new int[]{i, j}) ) .collect(Collectors.toList());
1.3 查找和匹配:
anyMatch:
检查流中是否有一个元素匹配。
boolean java = strs.stream().anyMatch(x -> x.equals("java")); //trueallMatch: 流中元素必须匹配所有的。
boolean java = strs.stream().allMatch(x -> x.equals("java")); //flase
noneMacth: 流中没有条件元素。
boolean java = strs.stream().noneMatch(x -> x.equals("java"));//flase
三个操作运用了所谓的短路,这就是大家熟悉
的Java中 && 和 || 运算符短路在流中的版本。
findAny:查找任意符元素
findAny 方法将返回当前流中的任意元素.
Optional<String> any = strs.stream().filter(x->x.length()>4).findAny();
Optional的几个常用方法: 1,isPresnet() 是否有值 有返回true 否则false.
2,ifPresent(Consumer<T> block) 会在值存在的时候执行给定的代码块
3,T get()存在时返回值,否抛异常。
4,T orElse(T other) 存在时返回值,否则返回默认的值。
findFirst : 查找元素中的第一个
findAny 和 findFrist的区别是 findFirst的 查找第一个元素的并行限制很多效率不如findAny
1.4 归约:
reduce
归约
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); int sum = numbers.stream().reduce(0, (a, b) -> a + b)
reduce 接受两个参数:
一个初始值,这里是0;
一个 BinaryOperator<T> 来将两个元素结合起来产生一个新值,这里我们用的是
lambda (a, b) -> a + b 。
1.5数值流
List<Dish> menu=[{name:"糖醋排骨",calories:350},{ name:"西红柿鸡蛋",calories:181},{ name:"鸡蛋汤",calories:95}]
情况:求menu中的热量的和
------- int allCalories = menu.stream().map(Dish::getCalories). reduce(0,Integer::sum) ;
这段代码的问题是,它有一个暗含的装箱成本。
每个 Integer 都必须拆箱成一个原始类型.Java 8引入了三个原始类型特化流接口来解决这个问题:
IntStream 、 DoubleStream 和LongStream ,分别将流中的元素特化为 int 、 long 和 double ,从而避免了暗含的装箱成本
优化:
转换: lmapToInt()
------menu.stream().mapToInt(Dish::getCalories).sum()
其中mapToInt()会把Stream转换成IntStream流。 IntStream 还支持max,min,average等方法。
转换成Stream流 在用 boxed() 方法。
IntStream intStream = menu.stream().mapToInt(Dish::getCalories);
Stream<Integer> stream = intStream.boxed();
1-100符合的勾股数 勾股数 假如a指定值求
IntStream.rangeClosed(1, 100) .filter(b -> Math.sqrt(a*a + b*b) % 1 == 0).boxed().map(b -> new int[]{a, b, (int) Math.sqrt(a * a + b * b)});
这里有一个关键的假设:给出了 a 的值。 现在,只要已知 a 的值,你就有了一个可以生成勾股数的流。如何解决这个问题呢?就像 b 一样,你需要为 a 生成数值!
Stream<int[]> pythagoreanTriples =
IntStream.rangeClosed(1, 100).boxed().flatMap( a ->
IntStream.rangeClosed(a, 100).filter(b -> Math.sqrt(a*a + b*b) % 1 == 0).mapToObj(b ->new int[]{a, b, (int)Math.sqrt(a * a + b * b)}));
目前的解决办法并不是最优的,因为你要求两次平方根。让代码更为紧凑的一种可能的方法是,先生成所有的三元数 (a*a, b*b, a*a+b*b) ,然后再筛选符合条件的:
Stream<double[]> pythagoreanTriples2 =
IntStream.rangeClosed(1, 100).boxed().flatMap(a ->IntStream.rangeClosed(a, 100).mapToObj(b -> new double[]{a, b, Math.sqrt(a*a + b*b)}).filter(t -> t[2] % 1 == 0))
1.6构建流
1.6.1 由值创建流 Stream.of()
你可以使用静态方法 Stream.of ,通过显式值创建一个流
Stream<String> stream = Stream.of("Java 8 ", "Lambdas ", "In ", "Action");1.6.2由数组创建流 Arrays.stream(List list).stream.map(String::toUpperCase).forEach(System.out::println);
你可以使用 empty 得到一个空流,如下所示:Stream<String> emptyStream = Stream.empty();
1.6.3由文件生成流 Files.lines ()int[] numbers = {2, 3, 5, 7, 11, 13};
int sum = Arrays.stream(numbers).sum();
数数文件中有多少各不相同的单词
long uniqueWords = 0; try(Stream<String> lines = Files.lines(Paths.get("data.txt"), Charset.defaultCharset())){ uniqueWords = lines.flatMap(line -> Arrays.stream(line.split(" "))) .distinct().count(); } catch(IOException e){ //throw new Exception(e); }
你可以使用 Files.lines 得到一个流,其中的每个元素都是给定文件中的一行
1.6.4 由函数生成流
由 iterate 和 generate 产生的流会用给定的函数按需创建值,因此可以无穷无尽地计算下去!一般来说, 应该使用 limit(n) 来对这种流加以限制,以避免打印无穷多个值
1. 迭代 Stream.iterate()
我们先来看一个 iterate 的简单例子,然后再解释:
Stream.iterate(0, n -> n + 2).limit(10).forEach(System.out::println);
iterate 方法接受一个初始值(在这里是 0 ),还有一个依次应用在每个产生的新值上的
Lambda( UnaryOperator<t> 类型)。这里,我们使用Lambda n -> n + 2 ,我们使用 limit 方法来显式限制流的大小。这里只选择了前10个偶数.
2.生成 Stream.generate()
Stream.generate(Math::random).limit(5).forEach(System.out::println);
generate它接受一个 Supplier<T> 类型的Lambda提供新的值
转载请 标明地址。本文可能有错误或者不足 请大家指出。 本博客是根据《java8实战》的总结笔记。更多内容可以查看此书。
-----------------BY DavinQi 。