zoukankan      html  css  js  c++  java
  • 流API--流的基础知识

    • 流接口--BaseStream接口
    流API定义了几个流接口,这些接口包含在java.util.stream中。BaseStream是基础接口,它定义了所有流都可以使用的基本功能。我们来看一下源码:
    public interface BaseStream<T, S extends BaseStream<T, S>> extends AutoCloseable {}
    这是一个泛型接口,T指定流中元素的类型,S指定扩展BaseStream的流的类型。BaseStream类extends了AutoCloseable接口,所以可以使用带资源的try语句管理流。在大多数的情况下,比如数据源是集合的时候,都不需要关闭流的。
    这个类基本实际编码中基本不会用到,里面的几个方法也都是些属性状态判断的方法,所以了解下就好了,这里不列出具体的API了。我们还要主要研究下Stream接口,几个基本类型的接口我们最后整理。


    • 流接口--Stream接口
    Stream也是一个接口,ReferencePipeline是它具体的实现类。具体的实现类,我们不用关心,因为一般都是直接使用这个接口的API就好了。Stream接口的定义如下:
    public interface Stream<T> extends BaseStream<T, Stream<T>> {}
    这个类里面好多方法我们要认真的了解下的,我们学习流API其实也就是学习这几个方法而已,在详细的整理的API之前,先来说3组概念。
    • 1,终端操作VS中间操作
    终端操作会消费流,这种操作作用于产生结果,例如找出流中最小的值,或者执行某种操作,比如forEach方法。一个流被消费后,就不能被重用了。
    中间操作会产生另外一个流,所以,中间操作可以用来创建执行一系列动作的管道。

    • 2,延迟行为
    延迟机制就是说某一种操作不是立即发生的。可能在中间的某一个时刻才会发生。上面的中间操作就不是立即发生的。当在中间操作创建的新流上执行终端操作后,中间操作指定的操作才会发生。之所有有这种机制,应该是出于性能原因吧,延迟行为可以让流API更加高效的执行

    • 3,有状态VS无状态
    无状态操作就是说,独立于其他元素来处理每一个元素。在有状态的操作中,每个元素的处理可能依赖于其他的元素。举个例子吧:比如说排序就应该是有状态操作,因为元素的顺序依赖于其他的元素的值。比如说过滤就应该是无状态操作,因为每个元素都是被单独处理的。当需要并行处理流的时候,无状态和有状态的区别最为重要,因为有状态操作可能需要多次处理才能完成。


    其实学习流API挺简单的,就是获取一个流,然后调相关的流API来操作就OK了。
    • 1,如何获取流?
    想要获取一个流,肯定要有一个数据源,这是实际编码中最常见的情景。
    1),如果数据源是集合的话,有2个方法可以获得一个流,下面是Collections的2个获取流的源码:
    //该方法默认返回一个顺序流
    		default Stream<E> stream() {
    	        return StreamSupport.stream(spliterator(), false);
    	    }
    
    
    	    //该方法默认返回一个并行流,如果无法获得一个并行流,也有可能返回一个顺序流
    	    default Stream<E> parallelStream() {
    	        return StreamSupport.stream(spliterator(), true);
    	    }

    2),如果数据源是数组的话,用Arrays工具类的一个stream()静态方法,
    public static <T> Stream<T> stream(T[] array) {
            return stream(array, 0, array.length);
        }

    该方法还有几个重载方法,用来返回处理基本类型的数组,他们返回的类型有IntStream,DoubleStream,LongStream。
    3),通过对一个流做中间操作来获取一个新的流。
    4),创建包含指定元素集合的流,使用of()方法。如果想要创建一个不包含任何元素的Stream,可以使用Stream的静态方法empty()。
    5),通过创建无限Stream的静态方法,generate()方法,iterate()方法


    关于获取流,下面整理一段演示代码:
    /**
     * @创建作者: LinkinPark
     * @创建时间: 2015年11月3日
     * @功能描述: 获取流
     */
    public class Test
    {
    
    
    	public static void main(String[] args)
    	{
    		//1,数据源是集合,从集合中获取一个流
    		List<Integer> list = new ArrayList<>(3);
    		list.add(1);
    		list.add(2);
    		list.add(3);
    		Stream<Integer> stream1 = list.stream();
    
    
    		//2,数据源是一个数组,从数组中获取一个流
    		Integer[] array = list.toArray(new Integer[0]);
    		Stream<Integer> stream2 = Arrays.stream(array);
    
    
    		//3,使用原来的一个流来生成一个新的流
    		Stream<Integer> stream3 = stream1.filter((i) -> true);
    
    
    		//4,直接使用Stream接口的静态方法of
    		Stream<Integer> stream4 = Stream.of(1, 2, 3);
    		stream4.forEach(System.out::println);
    
    
    		//Stream的静态方法empty()
    		Stream<String> emptyStream = Stream.empty();
    		//上面的这行代码会被编译器推导出来,和下面这行代码效果一样
    		Stream<String> emptyStream1 = Stream.<String> empty();
    
    
    		//5,Stream接口有2个静态方法,可以创建无限Stream
    		Stream<String> generate = Stream.generate(() -> "LinkinPark...");
    		Stream<Double> generate2 = Stream.generate(Math::random);
    		generate2.forEach(System.out::println);
    		Stream<BigInteger> iterate = Stream.iterate(BigInteger.ZERO, n->n.add(BigInteger.ONE));
    	}
    
    
    }


    • 2,OK,现在我来整理下具体的流的API:
    先来整理中间操作的方法:
    filter(),过滤掉Steam中所有不符合predicate接口的元素
    mapToXxx(),对流中的元素执行一对一的转换,该方法返回的新流中包含了ToXxxFunction接口的元素
    peek(),依次对每个元素执行一些操作,该方法返回的流与原来流包含相同的元素,该方法主要用于调试。
    distinct(),排序流中所有重复的元素,注意,这里元素重复的标准是使用equals()返回true。该方法有状态
    sorted(),保证流中的元素在后续的访问中出于有序状态。该方法有状态
    limit(),用于保证对流的后续访问中最大允许访问的元素个数。该方法有状态


    整理几个终端操作的方法:
    forEach(),遍历流中的所有元素,对每个元素执行Consumer接口
    toArray(),将流中所有的元素转换成一个数组
    reduce(),用于通过某种操作来合并流中的元素
    min(),返回流中所有元素的最小值
    max(),返回流中所有元素的最大值
    count(),返回流中所有元素的数量
    anyMatch(),判断流中是否至少包含一个元素符合Predicate接口
    allMatch(),判断流中是否每个元素都符合Predicate接口
    noneMatch(),判断流中是否所有元素都不符合Predicate接口
    findFirst(),返回流中的第一个元素
    findAny(),返回流中的任意一个元素


    • 3,一个简单的流示例

    OK,现在通过一段代码来做一个简单的流示例:

    public static void main(String[] args) throws Exception
    	{
    		//这里初始化一个list,下面Stream所有的操作都不会影响这个数据源的
    		List<Integer> list = new ArrayList<>(3);
    		list.add(1);
    		list.add(2);
    		list.add(3);
    		//获取一个流,来演示下min取最小值的操作
    		Stream<Integer> stream = list.stream();
    		Optional<Integer> min = stream.min(Integer::compare);
    		min.ifPresent(System.out::println);
    		//上面的min是终端操作,所以流被消费了。下面演示下链式操作,这也是Optional类和Stream流推荐的方式
    		list.stream().filter((value) -> value < 2).forEach(System.out::println);
    		list.stream().max(Integer::compare).filter((value) -> value > 5).orElseThrow(() -> new Exception("这里随便一个异常"));
    	}



  • 相关阅读:
    6.11 修饰符的适用范围
    5.10 对象与垃圾回收
    5.9 枚举类
    5.8 java 11增强的Lambda表达式
    5.7 内部类
    5.6 接口
    5.5 抽象类
    5.4 final修饰符
    5.3 类成员
    5.2 Object类中两个需要被重写的方法
  • 原文地址:https://www.cnblogs.com/LinkinPark/p/5232965.html
Copyright © 2011-2022 走看看