函数式编程 流Stream的使用

内部迭代与外部迭代

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

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

使用Iterator迭代

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

函数式编程 流Stream的使用_第1张图片

2.使用stream内部迭代

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

函数式编程 流Stream的使用_第2张图片

每种操作都对应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 collected = Stream.of("a", "b", "c").collect(Collectors.toList());
        Assert.assertEquals(Arrays.asList("a", "b", "c"), collected);
    }

map

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

函数式编程 流Stream的使用_第3张图片

    @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

函数式编程 流Stream的使用_第4张图片

    @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

函数式编程 流Stream的使用_第5张图片

    @Test
    public void testFlatMap() throws Exception {
        List 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 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

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

函数式编程 流Stream的使用_第6张图片

     @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 上的方法结合起来,可以完成很多常见的集合操作。


你可能感兴趣的:(函数式编程)