三. 使用流

    流的使用一般包括三件事:

        -- 一个数据源(如集合)来执行一个查询;

        -- 一个中间操作链,形成一条流的流水线;

        -- 一个终端操作,执行流水线,并能生成结果。

    1.流操作

        筛选、切片、映射、查找、匹配、归纳。(快速完成复杂数据查询)

    2.状态

        无状态操作:从输入流中得到0或1个结果。他们没有内部状态。如map、filter。

        有状态操作:先接受一个流再生成一个流时,需要知道先前的历史。

    3.Optional<T>类(java.util.Optional)是一个容器类,代表一个值存在或不存在。

        Optional里可以迫使你显式地检查值是否存在或处理值不存在的情形的方法:

            -- isPresent()将在Optional包含值的时候返回true,否则返回false;

            -- ifPresent(Consumer<T> block)会在值存在的时候执行给定的代码块;

            -- T get()会在值存在时返回值,否则抛出一个NoSuchElement异常;

            -- T orElse(T other)会在值存在时返回值,否则返回一个默认值。

操作描述操作类型返回类型使用的类型/函数式接口函数描述符说明

筛选

谓词筛选filter中间Stream<T>Predicate<T>T -> boolean
筛选各异元素distinct()中间(有状态-×××)Stream<T>

确保没有重复

切片

截短流limit(n)中间(有状态-有界)Stream<T>long

跳过元素skip(n)中间(有状态-有界)Stream<T>long
skip(n)与limit(n)互补

映射

对流中每一个元素应用函数map中间Stream<R>Function<T, R>T -> R接受一个函数作为参数
流的扁平化:各个数组并不是分别映射成一个流,而是映射成留的内容flatMap(Arrays::stream)中间Stream<R>Function<T, Stream<R>>T -> Stream<R>将[h,e,l,l,o],[w,o,r,l,d]变成[h,e,l,l,o,w,o,r,l,d]

排序sorted中间(有状态-×××)Stream<T>Comparator<T>(T, T) -> int

查找

返回当前流中的任意元素findAny终端Optional<T>

//如果包含一个值就返回它,否则什么都不做

menu.stream().filter(Dish::isvegetarian).findAny().ifPresent(d -> System.out.println(d.getName));

查找第一个元素findFirst终端Optional<T>


匹配

检查谓词是否匹配至少一个元素anyMatch终端booleanPredicate<T>T -> boolean

if(menu.stream().anyMatch(Dish::isVegetarian)) {

    System.out.println("The menu is (somewhat) vegetarian friendly!!");

}

检查谓词是否匹配所有元素allMatch终端booleanPredicate<T>T -> booleanboolean isHealthy = menu.stream().allMatch(d -> d.getCalories() < 1000);
没有任何元素与给定的谓词匹配noneMatch终端booleanPredicate<T>T -> booleanboolean isHealthy = menu.stream().noneMatch(d -> d.getCalories() >= 1000);

归约

对流中的数字求和reduce终端(有状态,有界)Optional<T>BinaryOperator<T>(T, T) -> T

//有初始值

int sum = numbers.stream().reduce(0, Integer :: sum);

//无初始值,返回Optional是考虑流中没有任何元素的情况,reduce元素无法返回其和

Optional<Integer> sum = numbers.stream().reduce((a, b) -> a * b);

最大值reduce(Integer::max)终端(有状态,有界)Optional<T>

Optional<Integer> max = numbers.stream().reduce(Integer::max);
最小值reduce(Integer::min)终端(有状态,有界)Optional<T>

Optional<Integer> min = numbers.stream().reduce(Integer::min);

collect终端RCollector<T, A, R>T -> void


forEach终端voidConsumer<T>


计算流中元素个数count终端long

long count = menu.stream().count();

eg:

public void testList() {
    List<Integer> list1 = Arrays.asList(1,2,3);
    List<Integer> list2 = Arrays.asList(3,4);
    
    //给定列表[1,2,3],[3,4]返回[(1,3),(1,4),(2,3),(2,4),(3,3),(3,4)]
    List<int[]> result = list1.stream()
        .flatMap(i -> list2.stream().map(j -> new int[]{i, j})
        ).collect(Collectors.toList());
        
    //给定列表[1,2,3],[3,4]返回总和能被3整除的数对:(2,4)和(3,3)
    List<int[]> result1 = list1.stream()
            .flatMap(i -> list2.stream().filter(j -> (i + j) % 3 == 0)
            .map(j -> new int[]{i, j})
        )
        .collect(Collectors.toList());
}


    4.特殊的流

        数值流

        原始类型流特化:特化是为了避免装箱造成的复杂性(类似int和Integer之间的效率差异)。

原始类型特化流接口映射到数值流将数值流转换回对象流Optional原始类型特化
IntStreammapToIntboxed()OptionalInt
DoubleStreammapToDoubleboxed()OptionalDouble
LongStreammapToLongboxed()OptionalLong

        数值范围:IntStream和LongStream有静态方法帮助生成这种范围:range(接受起始值)和rangeClosed(接受结束值)。


    5.构建流

创建流的方法说明举例
Stream.of通过显式值创建流Stream.of("hello", "world")
Stream.empty得到空流Stream.empty()
Arrays.stream由数组创建流

int[] numbers = {2,3,4,5};

int sum = Arrays.stream(numbers).sum();

Files.lines由文件生成流,其中每个元素都是给定文件中的一行

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) {}

Stream.iterate由函数生成流:创建无限流,使用limit()方法限制流的大小

//依次对每个新生成的值应用函数

Stream.iterate(0, n -> n + 2).limit(10).forEach(System.out::println);

Stream.generate

//接受一个Supplier<T>类型的Lambda提供新的值

IntStream tows = IntStream.generate(new IntSupplier() {

public int getAsInt() {

return 2;

}

});