java8学习笔记目录:
本节将详细介绍Java8中的数值流、流的创建与Optional类的使用。
不知大家还记不得,在介绍函数式编程接口中为了避免基础数据类型的装箱/拆箱带来的性能损耗,特意为函数式接口引入了基础数据类型的函数式编程接口,例如IntPredicate、LongPredicate、DoublePredicate。同样,流API也考虑到基本数据类型的装箱/拆箱会带来性能损耗,引入了数值流,例如IntStream、LongStream、DoubleStream。
java8中提供了3个原始数据特化流,分别为IntStream、LongStream、DoubleStream。本文将以IntStream进行讲解,其他流类似,只是数据类型分别代表Long或Double。
首先我们还是从一个示例开始本节的学习:计算菜单中所有菜品的卡路里之和。
1public static void test_reduce_caluli(List<Dish> menu) {
2 int calories = menu.stream()
3 .map(Dish::getCalories)
4 .reduce(0, Integer::sum);
5 System.out.println("菜品中的总卡路里:" + calories);
6}
上面包含了一个基本数据类型的装箱/拆箱动作,java8的流API提供了mapToInt方法,直接返回int类型的流
我们先稍微看一下mapToInt的方法声明:
1IntStream mapToInt(ToIntFunction<? super T> mapper)
2
接受一个T->int的函数式编程接口,直接返回IntStream流对象,而且IntStream本身提供了一些常用的聚合函数,例如sum。 使用IntStream来实现计算菜单中所有菜品的卡路里之和,其示例如下:
1public static void test_reduce_caluli_intStream(List<Dish> menu) {
2 int calories = menu.stream()
3 .mapToInt(Dish::getCalories)
4 .sum();
5 System.out.println("菜品中的总卡路里:" + calories);
6
7}
使用了特化流例如IntStream后,将不能再自动转换为其对应的封装对象流Stream< T >了,我们可以随意从IntStream对象中对应的通用方法的函数声明,例如IntStream#map函数的声明如下:
1IntStream map(IntUnaryOperator mapper);
只能接受int -> int的函数式编程接口,如果想将IntStream转回到Stream< Integer >,该如何处理呢?
IntStream提供了boxed()方法来实现将基础数据类型转换回对应的包装类型的流。
Stream中定义的方法,IntStream也可以使用,例如map、flatMap、distinict等,IntStream除这些之外,还提供了常用的聚合函数,例如sum、min、max、average(平均数)。
1OptionalDouble average();
2OptionalInt max();
3OptionalInt min();
4int sum();
有关Optional相关的类将在下文详细介绍。
另外除了上面提到的聚合函数,IntStream还提供了两个与数值范围的方法:
1public static IntStream range(int startInclusive, int endExclusive);
2public static IntStream rangeClosed(int startInclusive, int endExclusive);
rangeClosed与range的区别就是rangeClosed包含结束边界,举一个简单示例如下:
1public static void test_range() {
2 long count = IntStream.range(1,100)
3 .filter( i -> i % 2 == 0 )
4 .count();
5 System.out.println("count:" + count);
6
7}
计算【1,100)中包含的偶数个数,将输出49。如果将range(1,100)修改为rangeClosed(1,100),在输出的个数为50。
java 8的Stream提供了两个重载的of函数来显示的构建流,其声明如下:
1public static<T> Stream<T> of(T t)
2public static<T> Stream<T> of(T... values)
3
通过Arrays.stream构建流,其声明如下: Arrays#stream
1public static <T> Stream<T> stream(T[] array)
2
可以通过文件流创建流,在java.nio.file.Files类中定义了如下创建流的方法。
1public static Stream<Path> list(Path dir) throws IOException
2public static Stream<Path> walk(Path start, int maxDepth, FileVisitOption... options)
3public static Stream<Path> walk(Path start, FileVisitOption... options)
4public static Stream<Path> find(Path start, int maxDepth,BiPredicate<Path, BasicFileAttributes> matcher,
5 FileVisitOption... options)
6public static Stream<String> lines(Path path, Charset cs)
7public static Stream<String> lines(Path path) throws IOException
8
下面我们举一个示例:找出一个文件中不同词的个数。
1public static void test_file_stram() {
2 long uniqueWords = 0;
3 try(Stream<String> lines = Files.lines(Paths.get("d:/tmp/words.txt"), Charset.defaultCharset())) {
4 uniqueWords = lines.flatMap(line -> Arrays.stream(line.split("" )))
5 .distinct()
6 .count();
7
8 System.out.println("不重复字符个数:" + uniqueWords);
9 } catch (IOException e) {
10 e.printStackTrace();
11 }
12}
Stream API提供了两个静态方法从函数生成流:iterate、generate,我们先来看一下其函数声明:
1public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
2public static<T> Stream<T> generate(Supplier<T> s)
3
iterate方法的第一个参数类型为T,表示其初始值,第二个参数如下:
即其函数式声明为为T-T。其示例如下:
1public static void test_iterate() {
2 Stream.iterate(0, a -> a + 2)
3 .limit(10)
4 .forEach(System.out::println);
5}
注意:由于是无限流,故千万记得使用limit截断流,否则会无限循环下去。
其参数为Supplier< T >,其定义如下:
即构造一个T类型的对象,举例如下:
1public static void test_iterate() {
2 Stream.iterate(0, a -> a + 2)
3 .limit(10)
4 .forEach(System.out::println);
5}
这个在前面的示例中用的最多,就不做过多介绍。
为了更优雅的处理null值,避免空指针错误,java8中引入Optional类。
接下来对这些方法一一做个介绍。
本文就介绍到这里了,本文详细介绍了java8中的数值流、Stream的创建以及java8中Optional类的使用。