Java学习笔记 JDK8新特性(二)

简介: Java学习笔记 JDK8新特性(二)

二、Stream流


2.1、Stream的概述


Stream:指的是java.util.stream,将真正的函数式编程风格引入到java中,用于操作数据源(如集合、数组等)所产生的元素序列。


概述:Stream是Java8中处理集合的关键抽象概念,它可以指定你对集合进行的操作如复杂查找、过滤与映射数据等操作(类似于数据库筛选查询),也可以通过其进行并行操作。


Stream和Collection集合区别:Collection是一种静态的内存数据结构,而Stream是有关面向计算操作。前者是面向内存,存储在内容`中;后者面向CPU,通过CPU来实现计算。


操作三步骤:①首先有数据源(如集合,数组)获取一个流。②进行中间方法操作。③终止操作。


终止操作:一旦执行终止操作,就执行中间操作链,并产生结果,之后不会再使用。

注意点:


Stream自己不会存储元素。

Stream不会改变源对象,它会返回一个持有结果的新Stream流。

Stream操作时延迟执行的(懒加载),只有等到需要结果的时候才执行。


2.2、获取Stream方式(四种)


方式一:通过Collection接口扩展的方法获取stream


default Stream<E> stream():返回一个包含集合数据的顺序流。


default Stream<E> parallelStream() :返回一个包含集合数据的并行流。



方式二:通过数组,Arrays类中获取


public static <T> Stream<T> stream(T[] array):返回一个流。




方式三:通过Stream类的静态方法of()


static<T> Stream<T> of(T t):可传入单个任意类型。


static<T> Stream<T> of(T... values):可以接收任意数量的参数。



方式四:使用Stream类的静态方法创建无限流:就是根据不同形式无限生成出来对应类型的流


static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f):迭代形式。


seed:初始值。

f:迭代方式,例如t->t+2,每次+2。

static<T> Stream<T> generate(Supplier<T> s):生成形式。


实现T get();方法,不断返回值。

举例:


@Test
public void test() {
    //创建迭代流:输出10个数据
    Stream<Integer> iterate = Stream.iterate(0, t -> t + 2);
    iterate.limit(10).forEach((o)-> System.out.print(o+" "));//0 2 4 6 8 10 12 14 16 18
    //创建生成流:
    Stream<Double> generate = Stream.generate(Math::random);
    generate.limit(2).forEach(System.out::println);
    //0.3946065759053158
    //0.44714749442637125
}



2.3、Stream的中间操作


重要说明:由于Stream的中间操作方法返回的都是Stream<T>,所以我们可以连接多个方法操作形成一个流水线。需要注意的是除非流水线上触发终止操作,否则中间的筛选等操作不会执行任何处理,只会在终止操作时一次性全部处理,称为"惰性求值"。


筛选与切片


Stream<T> filter(Predicate<? super T> predicate):从流中排除某些元素。


Stream<T> distinct();:通过流中所生成元素的hashCode()和equals()方法取出重复元素。


Stream<T> limit(long maxSize);:截断流,使流中元素不会超过maxsize个。


Stream<T> skip(long n);:跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,就会返回空流(与limit方法互补)。



映射


<R> Stream<R> map(Function<? super T, ? extends R> mapper);:返回由给定函数应用于此流的元素的结果组成的流。


DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper);:返回一个DoubleStream,其中包含给定函数应用于此流的元素的结果。


其中的ToDoubleFunction接口方法会返回一个Double值

IntStream mapToInt(ToIntFunction<? super T> mapper);:返回一个IntStream,参数中的接口返回的是一个int值。


LongStream mapToLong(ToLongFunction<? super T> mapper);:与上面大致相同。



排序


Stream<T> sorted();:产生一个新流,会按照其中的元素自然排序排列。


Stream<T> sorted(Comparator<? super T> comparator);:产生一个新流,可以添加一个Comparator接口进行定制排序。



2.4、Stream的终止操作


说明:终止操作会从流的流水线上生成结果,其结果可以是任何不是流的值,例如List、Integer、void。进行终止操作时才开始执行中间操作,一旦进行了终止操作后,就不能再次使用了。


匹配与查找


boolean allMatch(Predicate<? super T> predicate);:检查是否匹配所有元素。


boolean anyMatch(Predicate<? super T> predicate);:检查是否至少匹配一个元素。


boolean noneMatch(Predicate<? super T> predicate);:检查是否匹配所有元素。


Optional<T> findFirst();:返回第一个元素。


Optional<T> findAny();:返回当前流中的任意元素。


long count();:返回流中元素总数。


Optional<T> max(Comparator<? super T> comparator);:返回流中的最大值,不过需要实现定制排序。


Optional<T> min(Comparator<? super T> comparator);:返回流中的最小值。



遍历迭代


void forEach(Consumer<? super T> action);:内部迭代。


我们之前对于Collection集合中的元素是使用iterator来进行手动获取其中的值,称为外部迭代。

使用该方法则时内部迭代,流中的每个元素都会放置在调用Comsumer接口的抽象方法中作为参数,你可以对其参数进行操作。一般来说直接遍历如stream.forEach(System.out::println())


归约


T reduce(T identity, BinaryOperator<T> accumulator);:将流中的元素反复集合起来(以及identity),返回一个值T。


//创建迭代流:输出10个数据
Stream<Integer> iterate = Stream.iterate(0, t -> t + 2);
Integer reduce = iterate.limit(2).reduce(5, Integer::sum);//7


Optional<T> reduce(BinaryOperator<T> accumulator);:将流中的元素集合起来,这里返回的是Optional。


说明:map与reduce的连接通常称为map-reduce模式,Google用它来进行网络搜索而出名。



收集


<R, A> R collect(Collector<? super T, A, R> collector);:将流转换为其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法。


参数中的接口可以使用Collectors中的方法来返回方法参数Collector接口。

Collector接口中的方法实现决定对流执行收集的操作。(如收集到List、Set、Map容器中)



该接口中包含了多个接口抽象方法。

我们再去看下Collectors类中的主要方法来搭配使用collect()方法:




引用尚硅谷课件中的图片。


三、Optional类


3.1、介绍Optional类


Optional类:能够很好解决空指针的问题。


该类是一个容器类,可以保存类型T的值,代表该值存在;若是保存为null,代表值不存在。

以前使用null表示一个值不存在,现在使用Optional更好表达这个概念,避免空指针异常。


public final class Optional<T> {
   ...
}




3.2、Optional的方式介绍


Optional类提供了许多有用的方法,其中就包括了检测控制,这样我们以后就不需要进行显式空值判断了。


创建Optional类对象方法


static <T> Optional<T> ofNullable(T value):value可以为null或非空,返回一个实例。


static <T> Optional<T> of(T value):value必须是非空否则会报NullPointerException。


static<T> Optional<T> empty() :创建一个空的Optional实例。



判断Optional容器中是否包含对象(可检测对象是否为null)


boolean isPresent() : 判断是否包含对象,若是不包含对象返回false,有对象返回为true。


void ifPresent(Consumer consumer) :如果有值,就执行Consumer 接口的实现代码,并且该值会作为参数传给它。



获取Optional容器的对象


T get():若容器中对象为null抛异常,若是不为null,返回该值。


T orElse(T other) :如果有值则将其返回,若是容器中对象为null返回指定的other对象。


T orElseGet(Supplier other) :如果有值则将其返回,若是容器中对象为null返回由 Supplier接口实现提供的对象。


T orElseThrow(Supplier exceptionSupplier) :如果有值则将其返回,若是容器中对象为null抛出由Supplier接口实现提供的异常。



3.3、Optional类源码分析


Optional类是如何来存储一个可以为null或不为null并进行判断的呢?


public final class Optional<T> {
    //通过使用value来判断
    private final T value;
    //1.1 使用of()方法获取一个Optional实例
    public static <T> Optional<T> of(T value) {
        //调用了1.2 有参构造
        return new Optional<>(value);
    }
    //1.2 有参构造
    private Optional(T value) {
        //调用Objects的方法来判断是否为null,一旦为null抛出异常,不为null返回实例给value接收
        this.value = Objects.requireNonNull(value);
    }
    //1.3 使用ofNullable()方法获取实例
    public static <T> Optional<T> ofNullable(T value) {
        //若是为空,调用1.4 empty()方法,不为空则直接调用1.1的of()方法
        return value == null ? empty() : of(value);
    }
    //1.4 该方法用于返回一个无参构造创建的Optional类,其中value值自然为null
    public static<T> Optional<T> empty() {
        @SuppressWarnings("unchecked")
        Optional<T> t = (Optional<T>) EMPTY;
        return t;
    }
    //get():若是容器中value为空抛出异常,不为空返回值value值
    public T get() {
        if (value == null) {
            throw new NoSuchElementException("No value present");
        }
        return value;
    }
}


Objects类:有参构造调用的方法


public static <T> T requireNonNull(T obj) {
    if (obj == null)
        throw new NullPointerException();
    return obj;
}


总结:


of(T value)方法中实际上会调用Object.requireNonNull方法,该方法能够判断若是为空则抛空指针。

ofNullable(T value)方法为什么传入null不抛异常呢?是因为检测到value为null时,返回了一个空的Optional构造器,其中的value自然也就为null了。

其余的相关方法看了源码之后就能一目了然。

相关文章
|
2月前
|
存储 安全 Java
Java Map新玩法:探索HashMap和TreeMap的高级特性,让你的代码更强大!
【10月更文挑战第17天】Java Map新玩法:探索HashMap和TreeMap的高级特性,让你的代码更强大!
78 2
|
11天前
|
存储 Java 开发者
什么是java的Compact Strings特性,什么情况下使用
Java 9引入了紧凑字符串特性,优化了字符串的内存使用。它通过将字符串从UTF-16字符数组改为字节数组存储,根据内容选择更节省内存的编码方式,通常能节省10%至15%的内存。
|
20天前
|
存储 Java 数据挖掘
Java 8 新特性之 Stream API:函数式编程风格的数据处理范式
Java 8 引入的 Stream API 提供了一种新的数据处理方式,支持函数式编程风格,能够高效、简洁地处理集合数据,实现过滤、映射、聚合等操作。
35 6
|
1月前
|
分布式计算 Java API
Java 8引入了流处理和函数式编程两大新特性
Java 8引入了流处理和函数式编程两大新特性。流处理提供了一种声明式的数据处理方式,使代码更简洁易读;函数式编程通过Lambda表达式和函数式接口,简化了代码书写,提高了灵活性。此外,Java 8还引入了Optional类、新的日期时间API等,进一步增强了编程能力。这些新特性使开发者能够编写更高效、更清晰的代码。
33 4
|
2月前
|
存储 Java API
优雅地使用Java Map,通过掌握其高级特性和技巧,让代码更简洁。
【10月更文挑战第19天】本文介绍了如何优雅地使用Java Map,通过掌握其高级特性和技巧,让代码更简洁。内容包括Map的初始化、使用Stream API处理Map、利用merge方法、使用ComputeIfAbsent和ComputeIfPresent,以及Map的默认方法。这些技巧不仅提高了代码的可读性和维护性,还提升了开发效率。
95 3
|
2月前
|
存储 安全 Java
Java Map新玩法:深入探讨HashMap和TreeMap的高级特性
【10月更文挑战第19天】Java Map新玩法:深入探讨HashMap和TreeMap的高级特性,包括初始容量与加载因子的优化、高效的遍历方法、线程安全性处理以及TreeMap的自然排序、自定义排序、范围查询等功能,助你提升代码性能与灵活性。
29 2
|
1月前
|
Java 数据库连接 API
Spring 框架的介绍(Java EE 学习笔记02)
Spring是一个由Rod Johnson开发的轻量级Java SE/EE一站式开源框架,旨在解决Java EE应用中的多种问题。它采用非侵入式设计,通过IoC和AOP技术简化了Java应用的开发流程,降低了组件间的耦合度,支持事务管理和多种框架的无缝集成,极大提升了开发效率和代码质量。Spring 5引入了响应式编程等新特性,进一步增强了框架的功能性和灵活性。
46 0
|
Java 测试技术
Java特性组合的通用方案
Java特性组合的通用方案
197 0
|
6天前
|
安全 Java API
java如何请求接口然后终止某个线程
通过本文的介绍,您应该能够理解如何在Java中请求接口并根据返回结果终止某个线程。合理使用标志位或 `interrupt`方法可以确保线程的安全终止,而处理好网络请求中的各种异常情况,可以提高程序的稳定性和可靠性。
36 6
|
21天前
|
设计模式 Java 开发者
Java多线程编程的陷阱与解决方案####
本文深入探讨了Java多线程编程中常见的问题及其解决策略。通过分析竞态条件、死锁、活锁等典型场景,并结合代码示例和实用技巧,帮助开发者有效避免这些陷阱,提升并发程序的稳定性和性能。 ####
下一篇
DataWorks