java8函数式编程(2)

/**
 * @author zhangdi
 * @description  常用的流操作
 */
public class LambdaChapter3_3 {
    public static void main(String[] args) {
        // new LambdaChapter3_3().testCollect();
        // new LambdaChapter3_3().testMap_usingMap();
        // new LambdaChapter3_3().testfilter_usingLambda();
        // new LambdaChapter3_3().testflatMap();
        new LambdaChapter3_3().testMaxAndMin();

    }
// 3.3.1 collect(toList()) :collect(toList()) 方法由 Stream 里的值生成一个列表,
    // 是一个及早求值操作。
    public void testCollect() {
        List<String> collected = Stream.of("a", "b", "c") // <1>
                .collect(Collectors.toList()); // <2>
        assertEquals(Arrays.asList("a", "b", "c"), collected); // <3>

    }

    // 3.3.2 map 如果有一个函数可以将一种类型的值转换成另外一种类型, map 操作就可以使用该函数, 将一个流中的值转换成一个新的流
    /**
     * 使用 for 循环将字符串转换为大写
     */
    public void testMap_usingFor() {
        List<String> collected = new ArrayList<>();
        for (String string : asList("a", "b", "hello")) {
            String upperCase = string.toUpperCase();
            collected.add(upperCase);
        }
        assertEquals(asList("A", "B", "HELLO"), collected);
    }

    /**
     * 使用 map 循环将字符串转换为大写
     */
    public void testMap_usingMap() {
        List<String> collected = Stream.of("a", "b", "hello").map(string -> string.toUpperCase())
                .collect(Collectors.toList());
    }

    // 3.3.3 filter ;遍历数据并检查其中的元素时, 可尝试使用 Stream 中提供的新方法 filter
    /**
     * 使用循环遍历列表, 使用条件语句做判断
     */
    public void testfilter_usingFor() {
        List<String> beginningWithNumbers = new ArrayList<>();
        for (String value : asList("Demacia", "Bilgewater", "2Valoran")) {
            if (isDigit(value.charAt(0))) {
                beginningWithNumbers.add(value);
            }
        }
        assertEquals(asList("2Valoran"), beginningWithNumbers);
    }

    /**
     * 函数式风格
     */
    public void testfilter_usingLambda() {
        List<String> beginningWithNumbers = Stream.of("Demacia", "Bilgewater", "2Valoran")
                .filter((value) -> isDigit(value.charAt(0))).collect(toList());
        assertEquals(asList("2Valoran"), beginningWithNumbers);
    }

    // 3.3.4 flatMap: flatMap 方法可用 Stream 替换值, 然后将多个 Stream 连接成一个 Stream
    // 前面已介绍过 map 操作, 它可用一个新的值代替 Stream 中的值。 但有时, 用户希望让 map
    // 操作有点变化, 生成一个新的 Stream 对象取而代之。
    /**
     * Question:假设有一个包含多个列表的流, 现在希望得到所有数字的序列。
     * 包含多个列表的 Stream
     */
    public void testflatMap() {
        List<Integer> together = Stream.of(asList(1, 2), asList(3, 6)).flatMap(value -> value.stream())
                .collect(toList());
        assertEquals(asList(1, 2, 3, 6), together);
    }

    // 3.3.5 max和min:Stream 上常用的操作之一是求最大值和最小值
    /**
     * 使用 Stream 查找最短曲目
     */
    public void testMaxAndMin() {
        List<Track> tracks = asList(new Track("Bakai", 524), new Track("Violets for Your Furs", 378),
                new Track("Time Was", 451));
        Track shortestTrack = tracks.stream().min(Comparator.comparing(track -> track.getLength())).get();

        /**这种new Track("Violets for Your Furs", 378)的方式会报错:Exception in thread "main" java.lang.AssertionError: 
         expected:<testLambda.LambdaChapter3_3$Track@5b8540fd> but was:<testLambda.LambdaChapter3_3$Track@f6dff17f>
        */
        // assertEquals(new Track("Violets for Your Furs", 378) ,shortestTrack);
        assertEquals(tracks.get(1), shortestTrack);
    }

    // 3.3.6 通用模式
    /**
     * 使用 for 循环查找最短曲目
     */
    public void testMaxAndMin_usingFor() {
        List<Track> tracks = asList(new Track("Bakai", 524), new Track("Violets for Your Furs", 378),
                new Track("Time Was", 451));
        Track shortestTrack = tracks.get(0);// 假定第一个track为最短曲目
        for (Track track : tracks) {
            if (track.getLength() < shortestTrack.getLength()) {
                shortestTrack = track;
            }
        }
        assertEquals(tracks.get(1), shortestTrack);

    }

    // 例 3-15 中的伪代码体现了通用模式的特点:reduce 模式 例3-15
    public void forkCode() {
        // Object accumulator = initialValue;
        // for (Object element : collection) {
        // accumulator = combine(accumulator, element);
        // }
    }

    // 3.3.7 reduce:reduce 操作可以实现从一组值中生成一个值。 在上述例子中用到的 count、 min 和 max 方法,
    // 因为常用而被纳入标准库中。 事实上, 这些方法都是 reduce 操作。
    /**
     * 使用 reduce(reduce的类型是BinaryOperator) 求和:将两个参数相加, acc 是累加器, 保存着当前的累加结果
     */
    public void testSum_usingReduce() {
        int count = Stream.of(1, 2, 3).reduce(0, (acc, element) -> acc + element);
        assertEquals(6, count);
    }

    // 例 3-17 展开 reduce 操作
    public void testReduce_expand_To_Sum() {
        BinaryOperator<Integer> accumulator = (acc, element) -> acc + element;
        int count = accumulator.apply(accumulator.apply(accumulator.apply(0, 1), 2), 3);
    }

    // 例 3-18 使用命令式编程方式求和 :在命令式编程方式下, 每一次循环将集合中的元素和累加器相加, 用相加后的结果更新累加器的值。
    public void testLambda_To_Sum() {
        int acc = 0;
        for (Integer element : asList(1, 2, 3)) {
            acc = acc + element;
            String str = "1";

        }
        assertEquals(6, acc);
    }

    // 3.3.8 整合操作 :如何将问题分解为简单的 Stream 操作
    // 第一个要解决的问题是, 找出某张专辑上所有乐队的国籍。
    /*
     * 问题步骤分解: 1. 找出专辑上的所有表演者。 2. 分辨出哪些表演者是乐队。 3. 找出每个乐队的国籍。 4. 将找出的国籍放入一个集合。
     */
    @Test
    public void findnNationality() {
        // Album album2 = SampleData.manyTrackAlbum;
        Album album = new Album("album_test",
                new ArrayList<Track>(Arrays.asList(new Track("track", 2), new Track("track2", 3))),
                new ArrayList<Artist>(Arrays.asList(new Artist("the tester", "china"), new Artist("haha", "USA")))

        );
        Set<String> set = album.getMusicians().filter((artist) -> artist.getName().startsWith("the"))// 假设所有乐队都以"the"开头.
                .map(artist -> artist.getNationality()).collect(Collectors.toSet());
        for (String string : set) {
            System.out.println("set:" + string);

        }
        // System.out.println("set:"+set.toArray().toString());
    }

    // 3.4 重构遗留代码
    // 例 3-19 遗留代码: 找出长度大于 1 分钟的曲目 (假定选定一组专辑, 找出其中所有长度大于 1 分钟的曲目名称。)
    public Set<String> findLongTracks(List<Album> albums) {
        Set<String> trackNames = new HashSet<>();
        for (Album album : albums) {
            for (Track track : album.getTrackList()) {
                if (track.getLength() > 60) {
                    String name = track.getName();
                    trackNames.add(name);
                }
            }
        }
        return trackNames;
    }

    // 例 3-20 重构的第一步: 找出长度大于 1 分钟的曲目
    public Set<String> findLongTracks_lambda_step1(List<Album> albums) {
        Set<String> trackNames = new HashSet<>();
        // 使用 Stream 的 forEach 方法替换掉 for 循环
        albums.stream().forEach(album -> {
            album.getTracks().forEach(track -> {
                if (track.getLength() > 60) {
                    trackNames.add(track.getName());
                }
            });
        });
        return trackNames;

    }

    // 例 3-21 重构的第二步
    public Set<String> findLongTracks_lambda_step2(List<Album> albums) {
        Set<String> trackNames = new HashSet<>();
        albums.stream().forEach(album -> {
            // 最内层的 forEach 方法有三个功用: 找出长度大于 1 分钟的曲目, 得到符合条件的曲目名称, 将曲目名称加入集合 Set
            album.getTracks().filter(track -> track.getLength() > 60).map(track -> track.getName())
                    .forEach(name -> trackNames.add(name));
        });
        return trackNames;
    }

    // 例 3-22 重构的第三步: 找出长度大于 1 分钟的曲目
    public Set<String> findLongTrackslambda_step3(List<Album> albums) {
        Set<String> trackNames = new HashSet<>();
        // 任何时候想转化或替代代码, 都该使用 map 操作。 这里将使用比 map 更复杂的 flatMap 操作, 把多个
        // Stream 合并成一个 Stream 并返回。
        albums.stream().flatMap(album -> album.getTracks()).filter(track -> track.getLength() > 60)
                .map(track -> track.getName()).forEach(name -> trackNames.add(name));
        return trackNames;
    }

    // 例 3-23 重构的第四步: 找出长度大于 1 分钟的曲目
    public Set<String> findLongTrackslambda_step4(List<Album> albums) {
        return albums.stream()
                .flatMap(album->album.getTracks())
                .filter(track->track.getLength()>60)
                .map(track->track.getName())
                .collect(Collectors.toSet());
    }


    //3.6 高阶函数:高阶函数是指接受另外一个函数作为参数, 或返回一个函数的函数。 


    //  3.8 要点回顾
    //  Ŗ 内部迭代将更多控制权交给了集合类。
    //  Ŗ 和 Iterator 类似, Stream 是一种内部迭代方式。
    //  Ŗ 将 Lambda 表达式和 Stream 上的方法结合起来, 可以完成很多常见的集合操作。

posted @ 2018-06-22 11:45  XueXueLai  阅读(138)  评论(0编辑  收藏  举报