三. 使用流

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

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

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

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

    1.流操作

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

    2.状态

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

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

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

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

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

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

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

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

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

筛选

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

确保没有重复

切片

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

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

映射

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

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

查找

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

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

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

查找第一个元素 findFirst 终端 Optional


匹配

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

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

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

}

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

归约

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

//有初始值

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

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

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

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

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

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

collect 终端 R Collector T -> void


forEach 终端 void Consumer


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

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

eg:

public void testList() {
    List list1 = Arrays.asList(1,2,3);
    List list2 = Arrays.asList(3,4);
    
    //给定列表[1,2,3],[3,4]返回[(1,3),(1,4),(2,3),(2,4),(3,3),(3,4)]
    List 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 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原始类型特化
IntStream mapToInt boxed() OptionalInt
DoubleStream mapToDouble boxed() OptionalDouble
LongStream mapToLong boxed() 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 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类型的Lambda提供新的值

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

public int getAsInt() {

return 2;

}

});