函数式编程 流Stream的使用

来源:互联网 发布:sql with as等价 编辑:程序博客网 时间:2024/05/16 11:36

内部迭代与外部迭代

使用for 循环计算来自伦敦的艺术家人数

  1. 外部迭代
int count = 0;for (Artist artist : allArtists) {    if (artist.isFrom("London")) {    count++;    }}

使用Iterator迭代

int count = 0;Iterator<Artist> iterator = allArtists.iterator();    while(iterator.hasNext()) {        Artist artist = iterator.next();        if (artist.isFrom("London")) {            count++;        }    }

这里写图片描述

2.使用stream内部迭代

long count = allArtists.stream().filter(artist -> artist.isFrom("London")).count();

这里写图片描述

每种操作都对应Stream 接口的一个方法。为了找出来自伦敦的艺术家,需要对Stream 对象进行过滤:filter。过滤在这里是指“只保留通过某项测试的对象”。测试由一个函数完成,根据艺术家是否来自伦敦,该函数返回true 或者false。由于Stream API 的函数式编程风格,我们并没有改变集合的内容,而是描述出Stream 里的内容。count() 方法计算给定Stream 里包含多少个对象。

惰性求值和及早求值

只过滤,不计数

allArtists.stream().filter(artist -> artist.isFrom("London"));

这行代码并未做什么实际性的工作,filter 只刻画出了Stream,但没有产生新的集合。像filter 这样只描述Stream,最终不产生新集合的方法叫作惰性求值方法;而像count 这样最终会从Stream 产生值的方法叫作及早求值方法。

由于使用了惰性求值,没有输出艺术家的名字:

allArtists.stream().filter(artist -> {    System.out.println(artist.getName());    return artist.isFrom("London");});

如果将同样的输出语句加入一个拥有终止操作的流,如例3-3 中的计数操作,艺术家的名字就会被输出

long count = allArtists.stream().filter(artist -> {    System.out.println(artist.getName());    return artist.isFrom("London");}).count();

判断一个操作是惰性求值还是及早求值很简单:只需看它的返回值。

如果返回值是Stream,那么是惰性求值;

如果返回值是另一个值或为空,那么就是及早求值。

常用的流操作

collect(toList())

collect(toList()) 方法由Stream 里的值生成一个列表,是一个及早求值操作。

    @Test    public void testCollect() throws Exception {        //Stream 的of 方法使用一组初始值生成新的Stream        List<String> collected = Stream.of("a", "b", "c").collect(Collectors.toList());        Assert.assertEquals(Arrays.asList("a", "b", "c"), collected);    }

map

如果有一个函数可以将一种类型的值转换成另外一种类型,map 操作就可以使用该函数,将一个流中的值转换成一个新的流。

这里写图片描述

    @Test    public void testMap() throws Exception {        //小写转大写        List<String> upperWords = new ArrayList<>();        //for循环实现        for (String s : Arrays.asList("a", "b", "c")) {            upperWords.add(s.toUpperCase());        }        Assert.assertEquals(Arrays.asList("A", "B", "C"), upperWords);        //lambda实现        upperWords = Stream.of("a", "b", "c")                .map(string -> string.toUpperCase())                .collect(Collectors.toList());        Assert.assertEquals(Arrays.asList("A", "B", "C"), upperWords);    }

filter

遍历数据并检查其中的元素时,可尝试使用Stream 中提供的新方法filter

这里写图片描述

    @Test    public void testFilter() throws Exception {        //判断是否以数字开头        List<String> beginWithNumber = new ArrayList<>();        //for循环实现        for (String s : Arrays.asList("1aa", "2bb", "cccc")) {            if (isDigit(s.charAt(0))) {                beginWithNumber.add(s);            }        }        Assert.assertEquals(Arrays.asList("1aa", "2bb"), beginWithNumber);        //lambda实现        beginWithNumber = Stream.of("1aa", "2bb", "cccc")                .filter(string -> isDigit(string.charAt(0)))                .collect(Collectors.toList());        Assert.assertEquals(Arrays.asList("1aa", "2bb"), beginWithNumber);    }

flatMap

flatMap 方法可用Stream 替换值, 然后将多个Stream 连接成一个Stream

这里写图片描述

    @Test    public void testFlatMap() throws Exception {        List<Integer> collect = Stream.of(Arrays.asList(1, 2, 3), Arrays.asList(4, 5, 6))                .flatMap(numbers -> numbers.stream())                .collect(Collectors.toList());        Assert.assertEquals(Arrays.asList(1, 2, 3, 4, 5, 6), collect);    }

max和min

求最大值和最小值

     @Test    public void testMaxAndMin() throws Exception {        List<Track> trackList = Arrays.asList(new Track("Bakai", 524),                new Track("Violets for Your Furs", 378),                new Track("Time Was", 451));        Track shotestTrack = trackList.stream()                .min(Comparator.comparing(track -> track.getLength()))                .get();        Track longestTrack = trackList.stream()                .max(Comparator.comparing(track -> track.getLength()))                .get();        Assert.assertEquals(trackList.get(1), shotestTrack);        Assert.assertEquals(trackList.get(0), longestTrack);    }

reduce

可以实现从一组值中生成一个值

这里写图片描述

     @Test    public void testReduce() throws Exception {        int sum = Stream.of(1, 2, 3, 4)                .reduce(0, (a, b) -> a + b);//0是初始值        Assert.assertEquals(10, sum);        int multi = Stream.of(10, 20, 30)                .reduce(1, (a, b) -> a * b);        Assert.assertEquals(6000, multi);        //将reduce 操作展开        BinaryOperator<Integer> accumulator = (acc, element) -> acc + element;        int count = accumulator.apply(                accumulator.apply(                        accumulator.apply(0, 1),                        2),                3);    }

高阶函数

高阶函数是指接受另外一个函数作为参数或返回一个函数的函数

高阶函数不难辨认:看函数签名就够了。

如果函数的参数列表里包含函数接口,或该函数返回一个函数接口,那么该函数就是高阶函数。

总结

  • 内部迭代将更多控制权交给了集合类。

  • 和 Iterator 类似,Stream 是一种内部迭代方式。

  • 将 Lambda表达式和 Stream 上的方法结合起来,可以完成很多常见的集合操作。


0 0
原创粉丝点击