java8实战读书笔记:数值流、Stream创建与Optional类的使用

java8学习笔记目录:

  1. java8实战读书笔记:Lambda表达式语法与函数式编程接口
  2. java8实战读书笔记:复合Lambda表达式
  3. java8实战读书笔记:初识Stream、流的基本操作(流计算)

本节将详细介绍Java8中的数值流、流的创建与Optional类的使用。

1、数值流

不知大家还记不得,在介绍函数式编程接口中为了避免基础数据类型的装箱/拆箱带来的性能损耗,特意为函数式接口引入了基础数据类型的函数式编程接口,例如IntPredicate、LongPredicate、DoublePredicate。同样,流API也考虑到基本数据类型的装箱/拆箱会带来性能损耗,引入了数值流,例如IntStream、LongStream、DoubleStream。

1.1 原始数据特化流

java8中提供了3个原始数据特化流,分别为IntStream、LongStream、DoubleStream。本文将以IntStream进行讲解,其他流类似,只是数据类型分别代表Long或Double。

1.1.1 映射到数据流

首先我们还是从一个示例开始本节的学习:计算菜单中所有菜品的卡路里之和。

public static void test_reduce_caluli(List<Dish> menu) {
    int calories = menu.stream()
                    .map(Dish::getCalories)
                    .reduce(0, Integer::sum);
    System.out.println("菜品中的总卡路里:" + calories);
}

上面包含了一个基本数据类型的装箱/拆箱动作,java8的流API提供了mapToInt方法,直接返回int类型的流

我们先稍微看一下mapToInt的方法声明:

IntStream mapToInt(ToIntFunction<? super T> mapper)

接受一个T->int的函数式编程接口,直接返回IntStream流对象,而且IntStream本身提供了一些常用的聚合函数,例如sum。
使用IntStream来实现计算菜单中所有菜品的卡路里之和,其示例如下:

public static void test_reduce_caluli_intStream(List<Dish> menu) {
    int calories = menu.stream()
            .mapToInt(Dish::getCalories)
            .sum();
    System.out.println("菜品中的总卡路里:" + calories);

}
1.1.2 转换回对象流

使用了特化流例如IntStream后,将不能再自动转换为其对应的封装对象流Stream< T >了,我们可以随意从IntStream对象中对应的通用方法的函数声明,例如IntStream#map函数的声明如下:

IntStream map(IntUnaryOperator mapper);

只能接受int -> int的函数式编程接口,如果想将IntStream转回到Stream< Integer >,该如何处理呢?

IntStream提供了boxed()方法来实现将基础数据类型转换回对应的包装类型的流。

1.1.3 常用函数

Stream中定义的方法,IntStream也可以使用,例如map、flatMap、distinict等,IntStream除这些之外,还提供了常用的聚合函数,例如sum、min、max、average(平均数)。

OptionalDouble average();
OptionalInt max();
OptionalInt min();
int sum();

有关Optional相关的类将在下文详细介绍。

另外除了上面提到的聚合函数,IntStream还提供了两个与数值范围的方法:

public static IntStream range(int startInclusive, int endExclusive);
public static IntStream rangeClosed(int startInclusive, int endExclusive);

rangeClosed与range的区别就是rangeClosed包含结束边界,举一个简单示例如下:

public static void test_range() {
    long count = IntStream.range(1,100)
                        .filter( i -> i % 2 == 0 )
                         .count();
    System.out.println("count:" + count);

}

计算【1,100)中包含的偶数个数,将输出49。如果将range(1,100)修改为rangeClosed(1,100),在输出的个数为50。

2、构建流

2.1 通过值构建流

java 8的Stream提供了两个重载的of函数来显示的构建流,其声明如下:

public static<T> Stream<T> of(T t)
public static<T> Stream<T> of(T... values)

2.2 通过数组构建流

通过Arrays.stream构建流,其声明如下:
Arrays#stream

public static <T> Stream<T> stream(T[] array)

2.3 通过文件流

可以通过文件流创建流,在java.nio.file.Files类中定义了如下创建流的方法。

public static Stream<Path> list(Path dir) throws IOException
public static Stream<Path> walk(Path start, int maxDepth, FileVisitOption... options)
public static Stream<Path> walk(Path start, FileVisitOption... options)
public static Stream<Path> find(Path start, int maxDepth,BiPredicate<Path, BasicFileAttributes> matcher,   
    FileVisitOption... options)
public static Stream<String> lines(Path path, Charset cs)
public static Stream<String> lines(Path path) throws IOException

下面我们举一个示例:找出一个文件中不同词的个数。

public static void test_file_stram() {
    long uniqueWords = 0;
    try(Stream<String> lines = Files.lines(Paths.get("d:/tmp/words.txt"), Charset.defaultCharset())) {  
        uniqueWords = lines.flatMap(line -> Arrays.stream(line.split("" )))
                .distinct()
                .count();

        System.out.println("不重复字符个数:" + uniqueWords);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

2.4 函数生成流:创建无限流

Stream API提供了两个静态方法从函数生成流:iterate、generate,我们先来看一下其函数声明:

public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
public static<T> Stream<T> generate(Supplier<T> s)
2.4.1 iterate

iterate方法的第一个参数类型为T,表示其初始值,第二个参数如下:
在这里插入图片描述
即其函数式声明为为T-T。其示例如下:

public static void test_iterate() {
    Stream.iterate(0, a -> a + 2)
            .limit(10)
            .forEach(System.out::println);
}

注意:由于是无限流,故千万记得使用limit截断流,否则会无限循环下去。

2.4.2 generate

其参数为Supplier< T >,其定义如下:
在这里插入图片描述
即构造一个T类型的对象,举例如下:

public static void test_iterate() {
    Stream.iterate(0, a -> a + 2)
            .limit(10)
            .forEach(System.out::println);
}

2.5 集合对象的stream

这个在前面的示例中用的最多,就不做过多介绍。

3、Optional类

为了更优雅的处理null值,避免空指针错误,java8中引入Optional类。
在这里插入图片描述
接下来对这些方法一一做个介绍。

  • public static< T> Optional< T> empty()
    创建一个Optional对象,其内部持有的对象为null。
  • public static < T > Optional< T > of(T value)
    使用value的值,创建一个Optional对象。
  • public static < T > Optional< T > ofNullable(T value)
    使用v去创建一个Optional对象,如果value为null,则返回empty()。
  • public T get()
    从Optional对象获取内嵌的对象,如果为空,则抛出NoSuchElementException。
  • public boolean isPresent()
    判断Optional对象中包含的值是否存在。
  • public void ifPresent(Consumer<? super T> consumer)
    如果Optional包裹的对象存在,则消费该对象。Consumer<?>的函数式编程接口:T -> void。
  • public Optional< T > filter(Predicate<? super T> predicate)
    如果Optional中包裹的对象为空,则返回自身,否则如果包裹的对象满足predicate表达式,则返回自身,否则返回empty()。
  • public< U > Optional< U > map(Function<? super T, ? extends U> mapper)
    如果Optional对象中包裹的对象为空,则返回empty(),否则运用(T-U),包裹U,当然如果U为空,则返回empty()。
  • public< U > Optional< U > flatMap(Function<? super T, Optional< U >> mapper)
    如果Option对象中包裹的对象为空,则返回empty(),否则使用对Optional中的包裹的对象value应用Function,最终返回Optional对象。
  • public T orElse(T other)
    返回Optional中包裹的对象,如果其值为空,则返回other。
  • public T orElseGet(Supplier<? extends T> other)
    返回Optional中包裹的对象,如果其值为空,则返回 Supplier函数式编辑接口中创建的值。
  • public < X extends Throwable> T orElseThrow(Supplier< ? extends X> exceptionSupplier) throws X
    返回Optional中包裹的对象,如果其值为空,则抛出自定义一次,由Supplier函数式编程接口返回。

其示例代码如下:

public static void test_option() {
   Map<String, String> data = new HashMap<>();
   Optional<String> value = Optional.ofNullable(data.get("userName"));
   // 如果存在userName值,则输出
   value.ifPresent(System.out::println);  
}

本文就介绍到这里了,本文详细介绍了java8中的数值流、Stream的创建以及java8中Optional类的使用。


欢迎加笔者微信号(dingwpmz),加群探讨,笔者优质专栏目录:
1、源码分析RocketMQ专栏(40篇+)
2、源码分析Sentinel专栏(12篇+)
3、源码分析Dubbo专栏(28篇+)
4、源码分析Mybatis专栏
5、源码分析Netty专栏(18篇+)
6、源码分析JUC专栏
7、源码分析Elasticjob专栏
8、Elasticsearch专栏(20篇+)
9、源码分析MyCat专栏

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

中间件兴趣圈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值